Schema Validation
Problem
Runtime validation is hand-written with manual type guards, creating verbose boilerplate that’s error-prone and hard to maintain. Validation logic and TypeScript types diverge over time because they’re defined separately, leading to mismatches between runtime checks and compile-time types. Complex nested objects require deeply nested validation code that’s difficult to read and test.
Solution
Define schemas that describe data structure and rules, validating against them at runtime. This catches data quality issues early and documents expected formats.
Example
This demonstrates using Zod to define and validate data schemas, ensuring runtime data matches expected types and constraints while keeping validation logic in sync with TypeScript types.
import { z } from 'zod';
// Define schema with validation rules
const userSchema = z.object({
name: z.string(), // Must be a string
email: z.string().email(), // Must be valid email format
age: z.number().min(0) // Must be non-negative number
});
// Parse and validate data - throws error if validation fails
const user = userSchema.parse(data);
Benefits
- Provides declarative validation that’s easier to read and maintain than manual checks.
- Keeps validation logic and types in sync when using type inference.
- Reduces boilerplate compared to hand-written validation code.
- Documents data structure and constraints in a single definition.
Tradeoffs
- Adds a dependency on a validation library.
- Can have a learning curve for schema definition syntax.
- May add runtime overhead for validating complex schemas.
- Error messages can be cryptic without custom configuration.