Zod project
Runtime schema validation library with heavily recursive conditional generics across builder chains; stresses object-guard class/generic identity and recursive schema inference.
tsz unavailable 4333 lines 115 KB tsz error; tsc ok
Timing
README
Zod
⭐️ smash that star button ⭐️
Like typesafety? Check out tRPC — a better way to build end-to-end typesafe APIs without GraphQL or code generation — just TypeScript.
Table of contents
- What is Zod
- Installation
- Ecosystem
- Basic usage
- Defining schemas
- Schema methods
- Type inference
- Errors
- Comparison
- Changelog
What is Zod
Zod is a TypeScript-first schema declaration and validation library. I'm using the term "schema" to broadly refer to any data type, from a simple string to a complex nested object.
Zod is designed to be as developer-friendly as possible. The goal is to eliminate duplicative type declarations. With Zod, you declare a validator once and Zod will automatically infer the static TypeScript type. It's easy to compose simpler types into complex data structures.
Some other great aspects:
- Zero dependencies
- Works in Node.js and browsers (including IE 11)
- Tiny: 8kb minified + zipped
- Immutable: methods (i.e.
.optional()return a new instance - Concise, chainable interface
- Functional approach: parse, don't validate
- Works with plain JavaScript too! You don't need to use TypeScript.
Sponsorship
Sponsorship at any level is appreciated and encouraged. Zod is maintained by a solo developer (hi!). For individual developers, consider the Cup of Coffee tier. If you built a paid product using Zod, consider the Startup tier. You can learn more about the tiers at github.com/sponsors/colinhacks.
Sponsors
|
Deletype deletype.com/ |
Kevin Simper @kevinsimper |
Brandon Bayer @flybayer, creator of Blitz.js |
|
Bamboo Creative bamboocreative.nz |
Jeremy Banks github.com/jeremyBanks |
Marcato Partners marcatopartners.com |
To get your name + Twitter + website here, sponsor Zod at the Freelancer or Consultancy tier.
Installation
To install Zod v3:
npm install zod
⚠️ IMPORTANT: You must enable strict mode in your tsconfig.json. This is a best practice for all TypeScript projects.
// tsconfig.json
{
// ...
"compilerOptions": {
// ...
"strict": true
}
}
TypeScript requirements
- Zod 3.x requires TypeScript 4.1+
- Zod 2.x requires TypeScript 3.7+
- Zod 1.x requires TypeScript 3.3+
Ecosystem
There are a growing number of tools that are built atop or support Zod natively! If you've built a tool or library on top of Zod, tell me about it on Twitter or start a Discussion. I'll add it below and tweet it out.
tRPC: Build end-to-end typesafe APIs without GraphQLts-to-zod: Convert TypeScript definitions into Zod schemas.zod-mocking: Generate mock data from your Zod schemas.zod-fast-check: Generatefast-checkarbitraries from Zod schemas.zod-endpoints: Contract-first strictly typed endpoints with Zod. OpenAPI compatible.express-zod-api: Build Express-based APIs with I/O schema validation and custom middlewareszod-to-json-schema: Convert your Zod schemas into JSON Schemas.json-to-zod: Convert JSON objects into Zod schemas. Use it live here.
Form integrations
react-hook-form: A first-party Zod resolver for React Hook Formformik: A community-maintained Formik adapter for Zod
Basic usage
Creating a simple string schema
import { z } from "zod";
// creating a schema for strings
const mySchema = z.string();
// parsing
mySchema.parse("tuna"); // => "tuna"
mySchema.parse(12); // => throws ZodError
// "safe" parsing (doesn't throw error if validation fails)
mySchema.safeParse("tuna"); // => { success: true; data: "tuna" }
mySchema.safeParse(12); // => { success: false; error: ZodError }
Creating an object schema
import { z } from "zod";
const User = z.object({
username: z.string(),
});
User.parse({ username: "Ludwig" });
// extract the inferred type
type User = z.infer<typeof User>;
// { username: string }
Defining schemas
Primitives
import { z } from "zod";
// primitive values
z.string();
z.number();
z.bigint();
z.boolean();
z.date();
// empty types
z.undefined();
z.null();
z.void(); // accepts undefined
// catch-all types
// allows any value
z.any();
z.unknown();
// never type
// allows no values
z.never();
Literals
const tuna = z.literal("tuna");
const twelve = z.literal(12);
const tru = z.literal(true);
// retrieve literal value
tuna.value; // "tuna"
Currently there is no support for Date or bigint literals in Zod. If you have a use case for this feature, please file an issue.
Strings
You can customize certain errors when creating a string schema.
const name = z.string({
required: "Name is required",
invalid: "Invalid name",
});
Zod includes a handful of string-specific validations.
z.string().max(5);
z.string().min(5);
z.string().length(5);
z.string().email();
z.string().url();
z.string().uuid();
z.string().cuid();
z.string().regex(regex);
// deprecated, equivalent to .min(1)
z.string().nonempty();
// optional custom error message
z.string().nonempty({ message: "Can't be empty" });
Check out validator.js for a bunch of other useful string validation functions.
Custom error messages
You can customize certain errors when creating a string schema.
const name = z.string({
required_error: "Name is required",
invalid_type_error: "Name must be a string",
});
When using validation methods, you can pass in an additional argument to provide a custom error message.
z.string().min(5, { message: "Must be 5 or more characters long" });
z.string().max(5, { message: "Must be 5 or fewer characters long" });
z.string().length(5, { message: "Must be exactly 5 characters long" });
z.string().email({ message: "Invalid email address" });
z.string().url({ message: "Invalid url" });
z.string().uuid({ message: "Invalid UUID" });
Numbers
You can customize certain error messages when creating a number schema.
const age = z.number({
required_error: "Age is required",
invalid_type_error: "Age must be a number",
});
Zod includes a handful of number-specific validations.
z.number().gt(5);
z.number().gte(5); // alias .min(5)
z.number().lt(5);
z.number().lte(5); // alias .max(5)
z.number().int(); // value must be an integer
z.number().positive(); // > 0
z.number().nonnegative(); // >= 0
z.number().negative(); // < 0
z.number().nonpositive(); // <= 0
z.number().multipleOf(5); // Evenly divisible by 5. Alias .step(5)
Optionally, you can pass in a second argument to provide a custom error message.
z.number().lte(5, { message: "this👏is👏too👏big" });
Booleans
You can customize certain error messages when creating a boolean schema.
const isActive = z.boolean({
required_error: "isActive is required",
invalid_type_error: "isActive must be a boolean",
});
Enums
There are two ways to define enums in Zod.
Zod enums
const FishEnum = z.enum(["Salmon", "Tuna", "Trout"]);
type FishEnum = z.infer<typeof FishEnum>;
// 'Salmon' | 'Tuna' | 'Trout'
You must pass the array of values directly into z.enum(). Alternatively, use as const to define your enum values as a tuple of strings. See the const assertion docs for details.
const VALUES = ["Salmon", "Tuna", "Trout"] as const;
const FishEnum = z.enum(VALUES);
This is not allowed:
const fish = ["Salmon", "Tuna", "Trout"];
const FishEnum = z.enum(fish);
In that case, the inferred type of fish is simply string[], so Zod isn't able to infer the individual enum elements.
Autocompletion
To get autocompletion with a Zod enum, use the .enum property of your schema:
FishEnum.enum.Salmon; // => autocompletes
FishEnum.enum;
/*
=> {
Salmon: "Salmon",
Tuna: "Tuna",
Trout: "Trout",
}
*/
You can also retrieve the list of options as a tuple with the .options property:
FishEnum.options; // ["Salmon", "Tuna", "Trout"]);
Native enums
Zod enums are the recommended approach to defining and validating enums. But if you need to validate against an enum from a third-party library (or you don't want to rewrite your existing enums) you can use z.nativeEnum() .
Numeric enums
enum Fruits {
Apple,
Banana,
}
const FruitEnum = z.nativeEnum(Fruits);
type FruitEnum = z.infer<typeof FruitEnum>; // Fruits
FruitEnum.parse(Fruits.Apple); // passes
FruitEnum.parse(Fruits.Banana); // passes
FruitEnum.parse(0); // passes
FruitEnum.parse(1); // passes
FruitEnum.parse(3); // fails
String enums
enum Fruits {
Apple = "apple",
Banana = "banana",
Cantaloupe, // you can mix numerical and string enums
}
const FruitEnum = z.nativeEnum(Fruits);
type FruitEnum = z.infer<typeof FruitEnum>; // Fruits
FruitEnum.parse(Fruits.Apple); // passes
FruitEnum.parse(Fruits.Cantaloupe); // passes
FruitEnum.parse("apple"); // passes
FruitEnum.parse("banana"); // passes
FruitEnum.parse(0); // passes
FruitEnum.parse("Cantaloupe"); // fails
Const enums
The .nativeEnum() function works for as const objects as well. ⚠️ as const required TypeScript 3.4+!
const Fruits = {
Apple: "apple",
Banana: "banana",
Cantaloupe: 3,
} as const;
const FruitEnum = z.nativeEnum(Fruits);
type FruitEnum = z.infer<typeof FruitEnum>; // "apple" | "banana" | 3
FruitEnum.parse("apple"); // passes
FruitEnum.parse("banana"); // passes
FruitEnum.parse(3); // passes
FruitEnum.parse("Cantaloupe"); // fails
Optionals
You can make any schema optional with z.optional():
const schema = z.optional(z.string());
schema.parse(undefined); // => returns undefined
type A = z.infer<typeof A>; // string | undefined
You can make an existing schema optional with the .optional() method:
const user = z.object({
username: z.string().optional(),
});
type C = z.infer<typeof C>; // { username?: string | undefined };
.unwrap
const stringSchema = z.string();
const optionalString = stringSchema.optional();
optionalString.unwrap() === stringSchema; // true
Nullables
Similarly, you can create nullable types like so:
const nullableString = z.nullable(z.string());
nullableString.parse("asdf"); // => "asdf"
nullableString.parse(null); // => null
You can make an existing schema nullable with the nullable method:
const E = z.string().nullable(); // equivalent to D
type E = z.infer<typeof D>; // string | null
.unwrap
const stringSchema = z.string();
const nullableString = stringSchema.nullable();
nullableString.unwrap() === stringSchema; // true
Objects
// all properties are required by default
const Dog = z.object({
name: z.string(),
age: z.number(),
});
// extract the inferred type like this
type Dog = z.infer<typeof Dog>;
// equivalent to:
type Dog = {
name: string;
age: number;
};
.shape
Use .shape to access the schemas for a particular key.
Dog.shape.name; // => string schema
Dog.shape.age; // => number schema
.extend
You can add additional fields an object schema with the .extend method.
const DogWithBreed = Dog.extend({
breed: z.string(),
});
You can use .extend to overwrite fields! Be careful with this power!
.merge
Equivalent to A.extend(B.shape).
const BaseTeacher = z.object({ students: z.array(z.string()) });
const HasID = z.object({ id: z.string() });
const Teacher = BaseTeacher.merge(HasID);
type Teacher = z.infer<typeof Teacher>; // => { students: string[], id: string }
If the two schemas share keys, the properties of B overrides the property of A. The returned schema also inherits the "unknownKeys" policy (strip/strict/passthrough) and the catchall schema of B.
.pick/.omit
Inspired by TypeScript's built-in Pick and Omit utility types, all Zod object schemas have .pick and .omit methods that return a modified version. Consider this Recipe schema:
const Recipe = z.object({
id: z.string(),
name: z.string(),
ingredients: z.array(z.string()),
});
To only keep certain keys, use .pick .
const JustTheName = Recipe.pick({ name: true });
type JustTheName = z.infer<typeof JustTheName>;
// => { name: string }
To remove certain keys, use .omit .
const NoIDRecipe = Recipe.omit({ id: true });
type NoIDRecipe = z.infer<typeof NoIDRecipe>;
// => { name: string, ingredients: string[] }
.partial
Inspired by
...