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