Schema validation, usually used with TypeScript.
You must enable strict
mode on tsconfig.json
.
npm install zod
Zod is also compatible with yarn, bun and pnpm.
import { z } from "zod";
const User = z.object({
name: z.string(),
});
// if you wanted the inferred type
type User = z.infer<typeof User>;
z.string().max(5);
z.string().min(5);
z.string().length(5);
z.string().email();
z.string().url();
z.string().emoji();
z.string().uuid();
z.string().cuid();
z.string().cuid2();
z.string().ulid();
z.string().regex(regex);
z.string().includes(string);
z.string().startsWith(string);
z.string().endsWith(string);
z.string().trim(); // trim whitespace
z.string().toLowerCase(); // toLowerCase
z.string().toUpperCase(); // toUpperCase
z.string().datetime(); // ISO 8601
const datetime = z.string().datetime();
datetime.parse("2020-01-01T00:00:00Z"); // pass
datetime.parse("2020-01-01T00:00:00.123Z"); // pass
datetime.parse("2020-01-01T00:00:00.123456Z"); // pass (arbitrary precision)
datetime.parse("2020-01-01T00:00:00+02:00"); // fail (no offsets allowed)
z.string().ip(); // IPv4/IPv6
const ip = z.string().ip();
ip.parse("192.168.1.1"); // pass
ip.parse("84d5:51a0:9114:1855:4cfa:f2d7:1f12:7003"); // pass
ip.parse("84d5:51a0:9114:1855:4cfa:f2d7:1f12:192.168.1.1"); // pass
ip.parse("256.1.1.1"); // fail
ip.parse("84d5:51a0:9114:gggg:4cfa:f2d7:1f12:7003"); // fail
// custom error messages
const age = z.number({
required_error: "Age is required",
invalid_type_error: "Age must be a number",
});
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)
z.number().finite(); // value must be finite, not Infinity or -Infinity
z.number().safe(); // value must be between Number.MIN_SAFE_INTEGER and Number.MAX_SAFE_INTEGER
z.date().min(new Date("1900-01-01"), { message: "Too old" });
z.date().max(new Date(), { message: "Too young!" });
// these two are the same
const stringArray = z.array(z.string());
const stringArray = z.string().array();
z.string().optional().array(); // (string | undefined)[]
z.string().array().optional(); // string[] | undefined
z.string().array().nonempty();
z.string().array().min(5); // must contain 5 or more items
z.string().array().max(5); // must contain 5 or fewer items
z.string().array().length(5); // must contain 5 items exactly
const Dog = z.object({
name: z.string(),
age: z.number(),
});
const BaseTeacher = z.object({ students: z.array(z.string()) });
const HasID = z.object({ id: z.string() });
const Teacher = BaseTeacher.merge(HasID);
const Recipe = z.object({
id: z.string(),
name: z.string(),
ingredients: z.array(z.string()),
});
const JustTheName = Recipe.pick({ name: true });
const NoIDRecipe = Recipe.omit({ id: true });
const user = z.object({
email: z.string(),
username: z.string(),
});
const partialUser = user.partial();
const user = z
.object({
email: z.string(),
username: z.string(),
})
.partial();
const requiredUser = user.required();
const trimmedLength = z
.function()
.args(z.string()) // accepts an arbitrary number of arguments
.returns(z.number())
.implement((x) => {
// TypeScript knows x is a string!
return x.trim().length;
});
trimmedLength("sandwich"); // => 8
trimmedLength(" asdf "); // => 4
numberPromise.parse("tuna");
// ZodError: Non-Promise type: string
numberPromise.parse(Promise.resolve("tuna"));
// => Promise<number>
const test = async () => {
await numberPromise.parse(Promise.resolve("tuna"));
// ZodError: Non-number type: string
await numberPromise.parse(Promise.resolve(3.14));
// => 3.14
};
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
const athleteSchema = z.tuple([
z.string(), // name
z.number(), // jersey number
z.object({
pointsScored: z.number(),
}), // statistics
]);
const stringOrNumber = z.union([z.string(), z.number()]);
stringOrNumber.parse("foo"); // passes
stringOrNumber.parse(14); // passes
const stringOrNumber = z.string().or(z.number());
z.set(z.string()).nonempty(); // must contain at least one item
z.set(z.string()).min(5); // must contain 5 or more items
z.set(z.string()).max(5); // must contain 5 or fewer items
z.set(z.string()).size(5); // must contain 5 items exactly
const stringNumberMap = z.map(z.string(), z.number());