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

tsz
failed
tsgo
n/a

README

Zod

Created by Colin McDonnell License npm stars coverage

⭐️ 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

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
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.

Form integrations

  • react-hook-form: A first-party Zod resolver for React Hook Form
  • formik: 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

...