Runtime validation, written like TypeScript
A literal is 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, separate
value
const User = { name: string }type
type User = Infer<typeof User>check
parse(User, input)Measured, not claimed
Performance and size
- 5.00 kB
- browser bundle, min + gzip
- 90.47M
- flat valid objects / second
- 341
- TypeScript instantiations
- 0
- runtime dependencies
Schemas are data
Composition
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
Compared with Zod
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
Ecosystem
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)