Skip to content

Commit ec56741

Browse files
committed
[FIX] validation: validate schema keys
This commit adds validation for the schema object itself, to ensure that only recognized keys are set on it. This is done to avoid typos when writing validation schemas (e.g. `{ type: Array, element*S*: String }`).
1 parent fb7d25b commit ec56741

File tree

2 files changed

+31
-13
lines changed

2 files changed

+31
-13
lines changed

src/runtime/validation.ts

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ function toSchema(spec: SimplifiedSchema): NormalizedSchema {
6060
);
6161
}
6262

63+
const SCHEMA_KEYS = new Set(["element", "optional", "shape", "type", "validate", "values"]);
64+
6365
/**
6466
* Main validate function
6567
*/
@@ -141,35 +143,42 @@ export function validateType(key: string, value: any, descr: TypeDescription): s
141143
let validDescr = descr.find((p) => !validateType(key, value, p));
142144
return validDescr ? null : `'${key}' is not a ${describe(descr)}`;
143145
}
144-
let result: string | null = null;
146+
const invalidKeys = Reflect.ownKeys(descr).filter((key) => !SCHEMA_KEYS.has(String(key)));
147+
if (invalidKeys.length) {
148+
return `invalid schema for '${key}': unknown keys ${invalidKeys
149+
.map((key) => `"${String(key)}"`)
150+
.join(", ")}`;
151+
}
145152
if ("element" in descr) {
146-
result = validateArrayType(key, value, descr.element!);
147-
} else if ("shape" in descr) {
153+
return validateArrayType(key, value, descr.element!);
154+
}
155+
if ("shape" in descr) {
148156
if (typeof value !== "object" || Array.isArray(value)) {
149-
result = `'${key}' is not an object`;
157+
return `'${key}' is not an object`;
150158
} else {
151159
const errors = validateSchema(value, descr.shape!);
152160
if (errors.length) {
153-
result = `'${key}' doesn't have the correct shape (${errors.join(", ")})`;
161+
return `'${key}' doesn't have the correct shape (${errors.join(", ")})`;
154162
}
155163
}
156-
} else if ("values" in descr) {
164+
}
165+
if ("values" in descr) {
157166
if (typeof value !== "object" || Array.isArray(value)) {
158-
result = `'${key}' is not an object`;
167+
return `'${key}' is not an object`;
159168
} else {
160169
const errors = Object.entries(value)
161170
.map(([key, value]) => validateType(key, value, descr.values!))
162171
.filter(Boolean);
163172
if (errors.length) {
164-
result = `some of the values in '${key}' are invalid (${errors.join(", ")})`;
173+
return `some of the values in '${key}' are invalid (${errors.join(", ")})`;
165174
}
166175
}
167176
}
168-
if ("type" in descr && !result) {
169-
result = validateType(key, value, descr.type!);
177+
if ("type" in descr) {
178+
return validateType(key, value, descr.type!);
170179
}
171-
if ("validate" in descr && !result) {
172-
result = !descr.validate!(value) ? `'${key}' is not valid` : null;
180+
if ("validate" in descr) {
181+
return !descr.validate!(value) ? `'${key}' is not valid` : null;
173182
}
174-
return result;
183+
return null;
175184
}

tests/validation.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,4 +308,13 @@ describe("validateSchema", () => {
308308
]);
309309
expect(validateSchema({ a: "string" }, { a: [String, { value: false }] })).toEqual([]);
310310
});
311+
312+
test("validate schema's own keys", () => {
313+
expect(validateSchema({ arr: [] }, { arr: { type: Array, elements: String } as any })).toEqual([
314+
`invalid schema for 'arr': unknown keys "elements"`,
315+
]);
316+
expect(validateSchema({ obj: {} }, { obj: { type: Object, shapes: String } as any })).toEqual([
317+
`invalid schema for 'obj': unknown keys "shapes"`,
318+
]);
319+
});
311320
});

0 commit comments

Comments
 (0)