PROJECT 01: Type-Safe Schema Validator
Build a zero-dependency type validation library from scratch. Validate nested object shapes, enforce strict primitives, and emit rich error diagnostics.
By the end, you’ll be able to…
- Architect a composable schema validator using first principles and closure factories.
- Enforce strict type-checking without relying on loose equality or dangerous coercion.
- Collect structured error diagnostics mapping property paths to validation failures.
01The Mission: A Pure Zero-Dependency Schema Engine
Every modern production application consumes untrusted external data: JSON payloads from HTTP endpoints, query strings, user inputs, or database records. Libraries like Zod and Joi are indispensable in modern stacks, but relying on them as black boxes obscures the core JavaScript mechanisms that power them.
In this project, you will build a lightweight, production-grade schema validation engine called SchemaValidator from scratch using vanilla ECMAScript. You will leverage closures, property introspection, strict type guards, and functional composition to validate complex nested data objects and emit detailed error reports.
02Architecting the Core Type Checkers
The foundation of any schema validator is rigorous primitive type inspection. In JavaScript, typeof null returns 'object' (a historic 1995 implementation quirk), and typeof [] returns 'object'. To validate with absolute precision, our engine must bypass these traps.
We construct base validators (string, number, boolean) as closure factories. Each validator maintains an internal pipeline of assertion functions. When validate(value) is called, it passes the value through these assertion checks, accumulating any validation rule failures into a structured diagnostics list.
At system boundaries—such as parsing JSON payloads received from HTTP endpoints, reading configuration files from disk, or deserializing messages from WebSocket streams—runtime data is untrusted. TypeScript types disappear completely at compile time, providing zero runtime protection against malformed payloads.
Our lightweight validator fills this exact void. By modeling validators as composable pipeline functions, we create a runtime type system that mirrors our compile-time expectations. If a client transmits a string where an integer is required, or omits a mandatory nested address object, our validator catches the violation at the network boundary, returning structured, user-friendly diagnostics before bad data corrupts our database.
03Building Nested Object Validation
With primitive validators in place, we implement the object schema validator. The object validator accepts a dictionary of child validators. When validating an input object, it verifies that the input is a non-null object, iterates through the schema keys, and recursively validates each child property.
By passing path segments (e.g. 'user.address.zipcode') down the recursive evaluation chain, the engine provides developer-friendly error messages that pinpoint the exact nested property responsible for any validation failure.
04Production Wrap-up and Acceptance Checks
Your completed Type-Safe Schema Validator is a fully autonomous engine ready to run in any browser or Node.js environment. It enforces strict type boundaries without external package bloat.
Verify your implementation against the three project milestones: it handles optional fields without throwing, validates primitive and nested object constraints accurately, and returns clear diagnostics for every detected schema violation.
The ideas worth keeping.
- Custom schema engines provide lightweight, zero-dependency validation for system boundaries.
- Avoid typeof null bugs by checking for objects explicitly with typeof val === 'object' && val !== null.
- Threading path strings down validation recursions produces readable, actionable diagnostics.
Make it yours.
Type and test your code right here in the interactive workspace. Run your solution, check the output, and compare with the worked guide when ready.
Exercise 1: Adding String Length Constraints
Extend a StringValidator with a .minLength(min) rule that adds an error if value.length < min.
Explore a worked solution
function addMinLength(validator, min) { validator.checks.push((val, path) => { if (typeof val === 'string' && val.length < min) { return { path, error: `Must be at least ${min} characters long` }; } return null; }); return validator;}Adding constraint closures to the validator pipeline allows chainable, composable rule definitions on the schema.
Exercise 2: Complete Project Runner
Write a complete functional test verifying that an invalid object triggers exactly two schema errors for bad type and missing required field.
Explore a worked solution
function runSchemaTest() { const schema = new ObjectValidator({ title: new BaseValidator((v, p) => typeof v === 'string' ? null : { path: p, error: 'Expected string' }), count: new BaseValidator((v, p) => typeof v === 'number' ? null : { path: p, error: 'Expected number' }) }); const result = schema.validate({ title: 123 }); // missing count, bad title return result.errors.length === 2;}The object schema accurately records both the type failure on 'title' and the missing required field failure on 'count'.
What stayed with you?
Check the ideas while they’re fresh. Your answers stay in this page and feedback appears as you go.
Exercise 1: Adding String Length Constraints
Extend a StringValidator with a .minLength(min) rule that adds an error if value.length < min.
[Process exited with code 0 - No output]
Keep exploring.
The official references behind this lesson, when you want a second angle or the full details.
Pause. Put the pieces together.
You’ve reached the end of Values, Types, Coercion & Equality Mechanics. Before moving on, use this task to check what you can do independently.
Complete the Type-Safe Schema Validator project validating arbitrary input objects.
Try it in your own environment and write down the result. This is a self-check; reading completion does not evaluate your code.
Review this moduleOne lesson closer.
Reading is a start. Trying it yourself makes the connection.