diff --git a/src/runtime/validation.ts b/src/runtime/validation.ts index 78594b6bc..168a228bc 100644 --- a/src/runtime/validation.ts +++ b/src/runtime/validation.ts @@ -60,6 +60,8 @@ function toSchema(spec: SimplifiedSchema): NormalizedSchema { ); } +const SCHEMA_KEYS = new Set(["element", "optional", "shape", "type", "validate", "values"]); + /** * Main validate function */ @@ -141,35 +143,42 @@ export function validateType(key: string, value: any, descr: TypeDescription): s let validDescr = descr.find((p) => !validateType(key, value, p)); return validDescr ? null : `'${key}' is not a ${describe(descr)}`; } - let result: string | null = null; + const invalidKeys = Reflect.ownKeys(descr).filter((key) => !SCHEMA_KEYS.has(String(key))); + if (invalidKeys.length) { + return `invalid schema for '${key}': unknown keys ${invalidKeys + .map((key) => `"${String(key)}"`) + .join(", ")}`; + } if ("element" in descr) { - result = validateArrayType(key, value, descr.element!); - } else if ("shape" in descr) { + return validateArrayType(key, value, descr.element!); + } + if ("shape" in descr) { if (typeof value !== "object" || Array.isArray(value)) { - result = `'${key}' is not an object`; + return `'${key}' is not an object`; } else { const errors = validateSchema(value, descr.shape!); if (errors.length) { - result = `'${key}' doesn't have the correct shape (${errors.join(", ")})`; + return `'${key}' doesn't have the correct shape (${errors.join(", ")})`; } } - } else if ("values" in descr) { + } + if ("values" in descr) { if (typeof value !== "object" || Array.isArray(value)) { - result = `'${key}' is not an object`; + return `'${key}' is not an object`; } else { const errors = Object.entries(value) .map(([key, value]) => validateType(key, value, descr.values!)) .filter(Boolean); if (errors.length) { - result = `some of the values in '${key}' are invalid (${errors.join(", ")})`; + return `some of the values in '${key}' are invalid (${errors.join(", ")})`; } } } - if ("type" in descr && !result) { - result = validateType(key, value, descr.type!); + if ("type" in descr) { + return validateType(key, value, descr.type!); } - if ("validate" in descr && !result) { - result = !descr.validate!(value) ? `'${key}' is not valid` : null; + if ("validate" in descr) { + return !descr.validate!(value) ? `'${key}' is not valid` : null; } - return result; + return null; } diff --git a/tests/validation.test.ts b/tests/validation.test.ts index b5e8bba12..29908272f 100644 --- a/tests/validation.test.ts +++ b/tests/validation.test.ts @@ -308,4 +308,13 @@ describe("validateSchema", () => { ]); expect(validateSchema({ a: "string" }, { a: [String, { value: false }] })).toEqual([]); }); + + test("validate schema's own keys", () => { + expect(validateSchema({ arr: [] }, { arr: { type: Array, elements: String } as any })).toEqual([ + `invalid schema for 'arr': unknown keys "elements"`, + ]); + expect(validateSchema({ obj: {} }, { obj: { type: Object, shapes: String } as any })).toEqual([ + `invalid schema for 'obj': unknown keys "shapes"`, + ]); + }); });