@@ -55,7 +55,7 @@ export type Validator<T> = {
5555function defaultCheck (
5656 validate : ( val : unknown ) => val is any ,
5757) : ( arg : unknown ) => CheckSchemaResult {
58- return ( arg ) => ( { unknownKeys : [ ] , valid : validate ( arg ) } ) ;
58+ return ( arg ) => ( { unknownKeys : [ ] , invalidKeys : [ ] , valid : validate ( arg ) } ) ;
5959}
6060
6161function makeValidator < T > (
@@ -88,20 +88,24 @@ export function array<T>(validator: Validator<T>) {
8888 check : ( val : unknown , path : string ) => {
8989 const result : CheckSchemaResult = successfulCheckSchema ( ) ;
9090
91+ // The value must be an array.
9192 if ( ! isArray ( val ) ) {
9293 result . valid = false ;
9394 return result ;
9495 }
9596
97+ // Validate all elements of the array.
9698 let index = 0 ;
9799 for ( const e of val ) {
98- const eResult = validator . check ( e , `${ path } [${ index } ].` ) ;
100+ const elementPath = `${ path } [${ index } ]` ;
101+ const eResult = validator . check ( e , `${ elementPath } ` ) ;
99102
100103 result . unknownKeys . push ( ...eResult . unknownKeys ) ;
101104 index ++ ;
102105
103106 if ( ! eResult . valid ) {
104107 result . valid = false ;
108+ result . invalidKeys . push ( elementPath ) ;
105109 continue ;
106110 }
107111 }
@@ -212,6 +216,8 @@ export interface CheckSchemaResult {
212216 valid : boolean ;
213217 /** Unknown keys that were found during validation. */
214218 unknownKeys : string [ ] ;
219+ /** Known keys that failed validation. */
220+ invalidKeys : string [ ] ;
215221}
216222
217223/**
@@ -221,6 +227,7 @@ function successfulCheckSchema(): CheckSchemaResult {
221227 return {
222228 valid : true ,
223229 unknownKeys : [ ] ,
230+ invalidKeys : [ ] ,
224231 } ;
225232}
226233
@@ -231,6 +238,7 @@ function invalidCheckSchema(): CheckSchemaResult {
231238 return {
232239 valid : false ,
233240 unknownKeys : [ ] ,
241+ invalidKeys : [ ] ,
234242 } ;
235243}
236244
@@ -242,13 +250,18 @@ export function checkSchema<S extends Schema>(
242250) : CheckSchemaResult {
243251 const result : CheckSchemaResult = successfulCheckSchema ( ) ;
244252 const inputKeys = new Set ( Object . keys ( obj ) ) ;
253+ const invalidKeys = new Set ( ) ;
245254
246255 for ( const [ key , validator ] of Object . entries ( schema ) ) {
247256 const hasKey = key in obj ;
248257
249258 // Remove key from set of unrecognised keys.
250259 inputKeys . delete ( key ) ;
251260
261+ // Add the key to the set of invalid keys. We remove it later once
262+ // it passes validation.
263+ invalidKeys . add ( key ) ;
264+
252265 // If the property is required, but absent, fail.
253266 if ( validator . required && ! hasKey ) {
254267 result . valid = false ;
@@ -271,9 +284,16 @@ export function checkSchema<S extends Schema>(
271284
272285 // If the property is present, validate it.
273286 if ( hasKey ) {
274- const checkResult = validator . check ( obj [ key ] , `${ path } ${ key } . ` ) ;
287+ const checkResult = validator . check ( obj [ key ] , `${ path } . ${ key } ` ) ;
275288
276289 result . unknownKeys . push ( ...checkResult . unknownKeys ) ;
290+ result . invalidKeys . push ( ...checkResult . invalidKeys ) ;
291+
292+ // If we have invalid keys from the validator, then that means that
293+ // we have a more specific key than `key`. Remove `key` from the results.
294+ if ( checkResult . invalidKeys . length > 0 ) {
295+ invalidKeys . delete ( key ) ;
296+ }
277297
278298 if ( ! checkResult . valid ) {
279299 result . valid = false ;
@@ -286,11 +306,17 @@ export function checkSchema<S extends Schema>(
286306 }
287307
288308 // If we reach this point, the key has been successfully validated.
309+ invalidKeys . delete ( key ) ;
289310 }
290311
291312 // If there are any remaining keys in `inputKeys`, add them to `unknownKeys`.
292313 for ( const remainingKey of inputKeys ) {
293- result . unknownKeys . push ( `${ path } ${ remainingKey } ` ) ;
314+ result . unknownKeys . push ( `${ path } .${ remainingKey } ` ) ;
315+ }
316+
317+ // If there are any remaining keys in `invalidKeys`, add them to the result.
318+ for ( const invalidKey of invalidKeys ) {
319+ result . invalidKeys . push ( `${ path } .${ invalidKey } ` ) ;
294320 }
295321
296322 return result ;
0 commit comments