Why @cleverbrush/schema?
One schema definition that drives your types, validation, API contracts, forms, and documentation — with zero duplication.
The Problem
In a typical TypeScript project, types and runtime validation live in separate worlds. You define a User interface in one file, then write validation rules in another — using Joi, Yup, Zod, or manual if checks. Over time these drift apart: the type says a field is required, but the validator allows undefined. Tests pass, but production breaks because the validation didn't match the type.
Then you need the same shape in your API contract, your form fields, your OpenAPI spec, and your object mapper. Each concern re-describes the same data shape — and each is another place for things to go wrong.
The Solution: Define Once, Use Everywhere
@cleverbrush/schema lets you define a schema once and derive everything from it:
- TypeScript types — via
InferType<typeof MySchema>, no duplicate interfaces - Runtime validation — with typed per-field error access
- API contracts — define endpoints that reference schemas, generate typed clients automatically
- React forms — headless form generation from schema PropertyDescriptors
- Object mapping — type-safe mapping between two schemas with compile-time completeness checks
- JSON Schema / OpenAPI — bidirectional conversion with
toJsonSchema()andfromJsonSchema()
import { object, string, number, type InferType } from '@cleverbrush/schema';
// Define once
const UserSchema = object({
name: string().minLength(2).describe('Display name'),
email: string().email().describe('Primary email'),
age: number().min(0).max(150)
});
// ── Types ─────────────────────────────────────────────
type User = InferType<typeof UserSchema>;
// { name: string; email: string; age: number }
// ── Validation ────────────────────────────────────────
const result = UserSchema.validate(untrustedInput);
if (!result.valid) {
const emailErrors = result.getErrorsFor(u => u.email); // typed!
}
// ── The same schema powers mapper, react-form, JSON Schema, server endpoints…One Schema, Many Consumers
The schema sits at the center of an ecosystem. Each companion package reads the schema's introspection data — no adapters, no code generation, no drift.
What No Other Schema Library Offers
If you know Zod, you already know most of the API. These three capabilities are what set @cleverbrush/schema apart:
1. Type-safe Extension System
Add your own methods to schema builders — fully typed, autocomplete-ready. The built-in .email(), .url(), .uuid() methods use the same public extension API. No other schema library supports this.
2. Generic / Parameterized Schemas
Create schema templates with type parameters using generic(). Apply them with .apply() to produce concrete schemas — with full introspection preserved. Competitors require plain functions that lose schema metadata.
Learn more about Generic Schemas →
3. PropertyDescriptors & Typed Field Navigation
Every schema exposes a typed property descriptor tree. Navigate fields with arrow functions like u => u.address.city — TypeScript verifies the path at compile time. This powers the mapper, react-form auto-generation, and typed error access. No magic strings, no runtime surprises.
Production Tested
Every form, every API response mapping, and every validation rule in cleverbrush.com/editor is powered by @cleverbrush/schema. It handles hundreds of schemas with nested objects, async validators, and custom error messages in production every day.
Next Steps
- Getting Started → — install and build your first schema
- Comparison → — detailed feature comparison with Zod, Valibot, and ArkType
- Playground → — try schemas live in the browser