Build validate(schema, value) — a validator that checks a runtime value against a schema, a plain-object description of the shape that value should have. This is the job a library like Zod or Joi does for you: an API endpoint receives a JSON request body, or a tool loads a config file, and before trusting that data you confirm every field has the right type, every required field is present, and nested objects and arrays match their declared shapes. Your validator handles primitives (string, number, boolean), nested object shapes, array shapes, and fields marked optional. Crucially, it does not stop at the first problem — it walks the entire value and reports every error, each tagged with a path saying exactly where it occurred.
// A Schema is a plain object describing an expected shape:
// { type: 'string' } — a primitive
// { type: 'number' }
// { type: 'boolean' }
// { type: 'object', fields: { <key>: Schema, ... } } — nested object
// { type: 'array', items: Schema } — homogeneous array
// Any field schema may also carry `optional: true`.
//
// returns: { valid: boolean, errors: Array<{ path: string, message: string }> }
// `valid` is true exactly when `errors` is empty.
// `path` locates the failure: '' for the root value, 'user.age' for a
// nested field, 'tags[2]' for an array element, 'tags[2].label' combined.
function validate(schema, value): { valid: boolean, errors: { path: string, message: string }[] };
// A valid nested object → no errors.
const schema = {
type: 'object',
fields: {
name: { type: 'string' },
age: { type: 'number' },
tags: { type: 'array', items: { type: 'string' } },
},
};
validate(schema, { name: 'Ada', age: 36, tags: ['math', 'code'] });
// → { valid: true, errors: [] }
// An invalid value collects MULTIPLE errors in one pass, each with a path.
validate(schema, { name: 42, age: 'old', tags: ['math', 7] });
// → {
// valid: false,
// errors: [
// { path: 'name', message: 'Expected string, got number' },
// { path: 'age', message: 'Expected number, got string' },
// { path: 'tags[1]', message: 'Expected string, got number' },
// ],
// }
string, number, boolean, object, and array. Primitive types compare with typeof; object and array recurse into fields and items respectively.optional: true. A missing required field is an error; a missing optional field is valid. A required field whose value is explicitly undefined is treated the same as missing — an error.fields produces an error at that key's path. (Some validators ignore unknown keys; this one rejects them.)user.age); array elements use a bracketed index (tags[2]); the two combine left to right (tags[2].label). The root value's path is the empty string ''.null is not an object here. typeof null === 'object' in JavaScript, but a null value where an object schema is expected is an error, not a valid empty object.Unlock the solution & editor
Runnable editor + tests
Solve in the browser with instant Jest feedback.
Detailed solutions
Walkthroughs, edge cases, and complexity notes.
Multi-framework variants
React, Vue, Vanilla, Angular — same question, different stacks.