跳到主要内容

zod 教程

基础

安装
pnpm add zod
基础用法
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 }

原子类型

import { z } from "zod";

// primitive values
z.string();
z.number();
z.bigint();
z.boolean();
z.date();
z.symbol();

// 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();

字面量类型

const tuna = z.literal("tuna");
const twelve = z.literal(12);
const twobig = z.literal(2n); // bigint literal
const tru = z.literal(true);

const terrificSymbol = Symbol("terrific");
const terrific = z.literal(terrificSymbol);

// retrieve literal value
tuna.value; // "tuna"

字符串

基本 API
// validations
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().datetime();
z.string().ip();

// transformations
z.string().trim(); // trim whitespace
z.string().toLowerCase(); // toLowerCase
z.string().toUpperCase(); // toUpperCase

// custom error message
z.string().min(5, { message: "Must be 5 or more characters long" });
ip 地址
const ipv4 = z.string().ip({ version: "v4" });
ipv4.parse("84d5:51a0:9114:1855:4cfa:f2d7:1f12:7003"); // fail

const ipv6 = z.string().ip({ version: "v6" });
ipv6.parse("192.168.1.1"); // fail

数字

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_S

// custom error message
z.number().lte(5, { message: "this is too big" });

BigInts

z.bigint().gt(5n);
z.bigint().gte(5n); // alias `.min(5n)`
z.bigint().lt(5n);
z.bigint().lte(5n); // alias `.max(5n)`

z.bigint().positive(); // > 0n
z.bigint().nonnegative(); // >= 0n
z.bigint().negative(); // < 0n
z.bigint().nonpositive(); // <= 0n

z.bigint().multipleOf(5n); // Evenly divisible by 5n.

NaN

const isNaN = z.nan({
required_error: "isNaN is required",
invalid_type_error: "isNaN must be not a number",
});

Boolean

const isActive = z.boolean({
required_error: "isActive is required",
invalid_type_error: "isActive must be a boolean",
});

Dates

// validate Date 示例
z.date().safeParse(new Date()); // success: true
z.date().safeParse("2022-01-12T00:00:00.000Z"); // success: false

// API
z.date().min(new Date("1900-01-01"), { message: "Too old" });
z.date().max(new Date(), { message: "Too young!" });

// custom error message
const myDateSchema = z.date({
required_error: "Please select a date and time",
invalid_type_error: "That's not a date!",
});

Native enum

enum Fruits {
Apple,
Banana,
}

const FruitEnum = z.nativeEnum(Fruits);
FruitEnum.parse(Fruits.Apple); // passes
FruitEnum.parse(Fruits.Banana); // passes
FruitEnum.parse(0); // passes
FruitEnum.parse(1); // passes
FruitEnum.parse(3); // fails

Optional

const schema = z.string().optional();
const user = z.object({
username: z.string().optional(),
});

Nullable

const E = z.string().nullable();
nullableString.parse("asdf"); // => "asdf"
nullableString.parse(null); // => null

Object

基础
const Dog = z.object({
name: z.string(),
age: z.number(),
});
.shape
  • 获得 key 模板类型;
Dog.shape.name; // => string schema
Dog.shape.age; // => number schema
.merge
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 }
.pick/.omit
  • 筛选和移除指定 key,生成新的 shema;
const Recipe = z.object({
id: z.string(),
name: z.string(),
ingredients: z.array(z.string()),
});

const JustTheName = Recipe.pick({ name: true }); // => { name: string }
const NoIDRecipe = Recipe.omit({ id: true }); // => { name: string, ingredients: string[] }
.partial
  • 浅复制;
  • 所有属性可选;
const user = z.object({
email: z.string()
username: z.string(),
});
// { email: string; username: string }

const partialUser = user.partial();
// { email?: string | undefined; username?: string | undefined }
.deepPartial
  • .partial 的深复制版本;
.required
  • 所有属性必选;
const user = z.object({
email: z.string()
username: z.string(),
}).partial();
// { email?: string | undefined; username?: string | undefined }

const requiredUser = user.required();
// { email: string; username: string }

Array

基础
const stringArray = z.string().array();
基本 API
// 返回模板数组类型
stringArray.element; // => string schema

// 至少一个元素
const nonEmptyStrings = 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

Tuples

const athleteSchema = z.tuple([
z.string(), // name
z.number(), // jersey number
z.object({
pointsScored: z.number(),
}), // statistics
]);

Unions

const stringOrNumber = z.union([z.string(), z.number()]);

stringOrNumber.parse("foo"); // passes
stringOrNumber.parse(14); // passes

Records

基础
const NumberCache = z.record(z.number());

type NumberCache = z.infer<typeof NumberCache>;
// => { [k: string]: number }
限制键
const NoEmptyKeysSchema = z.record(z.string().min(1), z.number());
NoEmptyKeysSchema.parse({ count: 1 }); // => { 'count': 1 }
NoEmptyKeysSchema.parse({ "": 1 }); // fails

Maps

const stringNumberMap = z.map(z.string(), z.number());

type StringNumberMap = z.infer<typeof stringNumberMap>;
// type StringNumberMap = Map<string, number>

Sets

const numberSet = z.set(z.number());
type NumberSet = z.infer<typeof numberSet>;
// type NumberSet = Set<number>

Promises

const numberPromise = z.promise(z.number());

Instanceof

class Test {
name: string;
}
const TestSchema = z.instanceof(Test);
const blob: any = "whatever";
TestSchema.parse(new Test()); // passes
TestSchema.parse("blob"); // throws

Functions

const myFunction = z
.function()
.args(z.string(), z.number()) // accepts an arbitrary number of arguments
.returns(z.boolean());

type myFunction = z.infer<typeof myFunction>;
// => (arg0: string, arg1: number)=>boolean