This module provides a flexible, TypeScript-friendly schema/type system for runtime validation, parsing, and type inference. It is inspired by libraries like valita and implements the Standard Schema specification.
- Define schemas for primitives, objects, arrays, tuples, unions, literals, records, and functions
- Parse and validate data at runtime
- Type inference for static TypeScript types
- Schema transformation:
.pick(),.omit(),.partial(),.required(),.extend() - Serialize/deserialize schemas to/from JSON
- Export schemas to TypeScript interfaces, Swift structs, or JSON Schema
- Parse environment variables and CLI arguments against a schema
- Standard Schema V1 compatible - works with tRPC, TanStack Form/Router, Hono, and other compatible libraries
All zeed schemas implement the StandardSchemaV1 interface, making them compatible with a growing ecosystem of tools and frameworks. This means you can use zeed schemas anywhere that accepts standard-schema-compliant validators.
Every schema has a ~standard property that provides the standard interface:
import { z } from 'zeed'
const schema = z.object({
name: z.string(),
age: z.number(),
})
// Access standard schema interface
const result = schema['~standard'].validate({ name: 'Alice', age: 30 })
if (result.issues) {
console.error('Validation errors:', result.issues)
}
else {
console.log('Valid data:', result.value)
}The ~standard property provides:
version: Always1(the specification version)vendor:'zeed'(identifies the schema library)validate(value): Synchronous validation function that returns either success or failuretypes: Type metadata for TypeScript type inference
import type { StandardSchemaV1 } from 'zeed'
import { z } from 'zeed'
const schema = z.object({
email: z.string(),
age: z.number().optional(),
})
// Extract input/output types via standard schema helpers
type Input = StandardSchemaV1.InferInput<typeof schema>
type Output = StandardSchemaV1.InferOutput<typeof schema>
// Or use zeed's own type inference
type User = z.infer<typeof schema>Zeed schemas work with any library that supports Standard Schema, including tRPC, TanStack Form, TanStack Router, Hono and more. See the full list.
import type { StandardSchemaV1 } from 'zeed'
import { z } from 'zeed'
function standardValidate<T extends StandardSchemaV1>(
schema: T,
data: unknown,
): StandardSchemaV1.InferOutput<T> {
const result = schema['~standard'].validate(data)
if (result.issues)
throw new Error(`Validation failed: ${JSON.stringify(result.issues)}`)
return result.value
}
const userSchema = z.object({ name: z.string() })
const user = standardValidate(userSchema, { name: 'Alice' })import { z } from 'zeed'
const name = z.string()
const age = z.number()
const count = z.int()
const flag = z.boolean()
const nothing = z.none() // undefined | null
const whatever = z.any()
// Numeric aliases for number()
const x = z.float()
const y = z.double()
const r = z.real()import { z } from 'zeed'
const user = z.object({
name: z.string(),
age: z.number().optional(),
})
// Type: { name: string; age?: number }import { z } from 'zeed'
const tags = z.array(z.string())
// Type: string[]
const pair = z.tuple([z.string(), z.number()])
// Type: [string, number]import { z } from 'zeed'
const scores = z.record(z.number())
// Type: Record<string, number>import { z } from 'zeed'
const status = z.union([
z.literal('active'),
z.literal('inactive'),
])
// Type: 'active' | 'inactive'
const role = z.enum(['admin', 'user', 'guest'])
// Type: 'admin' | 'user' | 'guest'
// Note: z.enum is an alias for stringLiteralsimport { z } from 'zeed'
const user = z.object({ name: z.string(), age: z.number().optional() })
type User = z.infer<typeof user>
// { name: string; age?: number }import { z } from 'zeed'
const user = z.object({ name: z.string(), age: z.number().optional() })
const parsed = user.parse({ name: 'Alice', age: 30 })import { z } from 'zeed'
const score = z.number().optional().default(0)
// Type: number | undefined (default: 0)
// Default can also be a function
const id = z.string().default(() => crypto.randomUUID())import { z } from 'zeed'
const name = z.string().meta({ desc: 'The user name' })
const age = z.number().describe('Age in years')import { z } from 'zeed'
const add = z.func([z.number(), z.number()], z.number())
// Type: (a: number, b: number) => number
const rpcCall = z.rpc(z.object({ id: z.string() }), z.number())
// Type: (info: { id: string }) => number | Promise<number>Object schemas support composition methods similar to Zod:
import { z } from 'zeed'
const user = z.object({
id: z.string(),
name: z.string(),
email: z.string(),
age: z.number(),
})
// Extend with additional fields
const userWithRole = user.extend({ role: z.string() })
// Pick a subset of fields
const userName = user.pick({ id: true, name: true })
// Omit fields
const userWithoutEmail = user.omit({ email: true })
// Make all (or selected) fields optional
const partialUser = user.partial()
const partialByKey = user.partial({ age: true })
// Make all (or selected) fields required
const requiredUser = partialUser.required()Serialize schemas to plain JSON and reconstruct them later. Useful for sending schemas over the network or persisting them.
import { deserializeSchema, serializeSchema, z } from 'zeed'
const userSchema = z.object({
name: z.string(),
age: z.number().optional(),
role: z.enum(['admin', 'user']).default('user'),
})
const serialized = serializeSchema(userSchema)
const jsonString = JSON.stringify(serialized)
const deserialized = deserializeSchema(JSON.parse(jsonString))
const user = deserialized.parse({ name: 'Alice', role: 'admin' })Note: Function defaults (e.g., .default(() => 'value')) cannot be serialized and are omitted. Only static default values are preserved.
Map a flat object schema onto process.env. Field names are converted from camelCase to UPPER_SNAKE_CASE.
import { parseSchemaEnv, stringFromSchemaEnv, z } from 'zeed'
const envSchema = z.object({
port: z.int().default(3000).meta({ envDesc: 'Server port' }),
dbUrl: z.string().meta({ envDesc: 'Database connection URL' }),
debug: z.boolean().default(false),
secret: z.string().meta({ envPrivate: true }),
})
const env = parseSchemaEnv(envSchema)
// Reads PORT, DB_URL, DEBUG, SECRET from process.env
// Generate a .env template (private fields are hidden unless showPrivate=true)
const template = stringFromSchemaEnv(envSchema)Supported meta fields: envDesc, envPrivate, envSkip.
import { helpSchemaArgs, parseSchemaArgs, z } from 'zeed'
const argsSchema = z.object({
input: z.string().meta({ argShort: 'i', argDesc: 'Input file' }),
verbose: z.boolean().default(false).meta({ argShort: 'v' }),
count: z.int().default(1),
})
const [args, rest] = parseSchemaArgs(argsSchema) // reads process.argv
const help = helpSchemaArgs(argsSchema)Supported meta fields: argShort, argDesc.
import { schemaExportTypescriptInterface, z } from 'zeed'
const user = z.object({
name: z.string(),
age: z.int().optional(),
})
const ts = schemaExportTypescriptInterface(user, 'User')
// export interface User { name: string; age?: number }import { schemaExportSwiftStruct, z } from 'zeed'
const user = z.object({
name: z.string(),
age: z.int().optional(),
})
const swift = schemaExportSwiftStruct(user, 'User')Supported meta fields: swiftName, swiftProtocol, swiftDesc, swiftDefault.
import { schemaExportJsonSchema, schemaExportJsonSchemaString, z } from 'zeed'
const user = z.object({
name: z.string(),
age: z.number().optional(),
})
const jsonSchema = schemaExportJsonSchema(user)
const jsonSchemaStr = schemaExportJsonSchemaString(user)z.string(),z.number(),z.int(),z.boolean(),z.none(),z.any()z.float(),z.double(),z.real()- aliases forz.number()
z.object({...}),z.array(type),z.tuple([...]),z.record(type)z.union([...])z.literal(value),z.stringLiterals([...]),z.enum([...])z.func(args, ret),z.rpc(info, ret)
.optional(),.default(value | fn).meta({ desc, ... }),.describe(msg).parse(obj)- Object only:
.extend({...}),.pick({...}),.omit({...}),.partial(),.partial({...}),.required(),.required({...})
z.infer<typeof schema>
serializeSchema(schema)- Convert a schema to a plain JSON objectdeserializeSchema(json)- Reconstruct a schema from a JSON object
schemaCreateObject(schema)- Build an object from defaultsschemaParseObject(schema, obj, opt?)- Parse and coerce an object against a schemaschemaValidateObject(schema, obj, opt?)- Validate without coercion, returns issues
parseSchemaEnv(schema, opt?),stringFromSchemaEnv(schema, prefix?, commentOut?, showPrivate?)parseSchemaArgs(schema, argv?),helpSchemaArgs(schema)
schemaExportTypescriptInterface(schema, name?)schemaExportSwiftStruct(schema, name?)schemaExportJsonSchema(schema),schemaExportJsonSchemaString(schema)
isSchemaObject(schema),isSchemaObjectFlat(schema)isSchemaOptional(schema),isSchemaDefault(schema),isSchemaPrimitive(schema)
z.string()and the barestring()export are functionally equivalent, butz.string()is preferred for consistency and clarity.- Each schema definition results in a corresponding TypeScript type, extractable via
z.infer<typeof schema>.