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.

Read the docs →
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

valueconst User = { name: string }
typetype User = Infer<typeof User>
checkparse(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
See contracts and methodology →

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.

TypeScript
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

Zod
const User = z.object({
  name: z.string().min(1),
  email: z.string().email().optional(),
})

type User = z.infer<typeof User>
User.parse(input)
litetype
const User = {
  name: string.min(1),
  'email?': string.email(),
}

type User = Infer<typeof User>
parse(User, input)
Move from Zod →

One standard edge

Ecosystem

Keep schemas bare inside your code. Wrap once with Standard Schema where another tool needs it.

TypeScript
const User = { name: string.min(1) }

t.procedure
  .input(standard(User))
  .query(({ input }) => input)
Integration recipes →

Install