Runtime validation, written like TypeScript
A literal is
already a schema.
No object builder. No method language. Write a value like an interface, infer its type, then validate unknown data.
runtimetype
const User = {
name: string.min(1),
age: number.min(0),
'email?': string.email(),
}
type User = Infer<typeof User>
parse(User, input)
The model
Three things, kept separate.
value
const User = { name: string }type
type User = Infer<typeof User>check
parse(User, input)Measured, not claimed
Small enough to disappear.
Fast enough to stop thinking about.
- 5.00 kB
- browser bundle, min + gzip
- 90.47M
- flat valid objects / second
- 341
- TypeScript instantiations
- 0
- runtime dependencies
Schemas are data
Compose with JavaScript.
Spread is extend. Property access is pick. Rest destructuring is omit. Optional keys remain optional because the language already knows how objects compose.
const User = { name: string, age: number, 'email?': string }
const Timestamps = { createdAt: date, 'deletedAt?': date }
const Post = { title: string, ...Timestamps }
const Public = { name: User.name, 'email?': User['email?'] }
const { age: _, ...NoAge } = User
Less library language
The same job, with the scaffolding removed.
const User = z.object({
name: z.string().min(1),
email: z.string().email().optional(),
})
type User = z.infer<typeof User>
User.parse(input)
const User = {
name: string.min(1),
'email?': string.email(),
}
type User = Infer<typeof User>
parse(User, input)
One standard edge
tRPC, Hono and forms
without adapter packages.
Keep schemas bare inside your code. Wrap once with Standard Schema where another tool needs it.
const User = { name: string.min(1) }
t.procedure
.input(standard(User))
.query(({ input }) => input)