Zod 校验库分析及使用说明
一、概述
Zod 是一个以 TypeScript 为首的模式声明和验证库(TypeScript-first schema validation with static type inference)。它解决了 TypeScript 类型信息在编译后丢失、无法在运行时校验数据的问题,将类型安全从编译时延伸到了运行时。
核心定位:你只需声明一次验证器(schema),Zod 就会自动推断出静态 TypeScript 类型,实现"一份定义,同时满足编译时类型检查和运行时数据校验"。
官方文档:https://zod.dev
二、核心特点
| 特点 | 说明 |
|---|---|
| TypeScript 优先 | 深度集成 TypeScript,自动类型推断 |
| 零依赖 | 无外部依赖,体积小巧(核心包压缩后仅 2KB) |
| 不可变 API | 所有方法(如 .optional())返回新实例,不修改原对象 |
| 跨平台 | 支持 Node.js 和所有现代浏览器 |
| 链式接口 | 简洁、可链式调用的 API 风格 |
| 内置 JSON Schema 转换 | 可将 Zod schema 转换为 JSON Schema |
三、安装
bash
npm install zod
# 或
yarn add zod
# 或
pnpm add zod
四、基本使用
4.1 定义 Schema
typescript
import * as z from "zod";
// 定义一个对象 schema
const UserSchema = z.object({
name: z.string(),
age: z.number().min(0),
email: z.string().email(),
});
4.2 解析数据(Parsing)
使用 .parse() 验证数据,验证成功返回强类型的深拷贝数据:
typescript
const user = UserSchema.parse({
name: "Alice",
age: 25,
email: "alice@example.com"
});
// user 的类型被自动推断为 { name: string; age: number; email: string }
4.3 错误处理
方式一:try/catch
typescript
try {
UserSchema.parse({ name: 42, age: "25" });
} catch (err) {
if (err instanceof z.ZodError) {
console.log(err.issues);
// 返回详细的错误信息数组,包含 path、message、code 等
}
}
方式二:safeParse(推荐)
使用 .safeParse() 避免 try/catch,返回一个可辨识联合类型(discriminated union)的结果对象:
typescript
const result = UserSchema.safeParse({ name: 42, age: "25" });
if (!result.success) {
console.log(result.error); // ZodError 实例
} else {
console.log(result.data); // 类型安全的验证通过数据
}
4.4 类型推断
使用 z.infer<> 从 schema 中提取 TypeScript 类型:
typescript
const UserSchema = z.object({
name: z.string(),
age: z.number(),
});
type User = z.infer<typeof UserSchema>;
// 等价于:type User = { name: string; age: number }
五、核心 API
5.1 基础类型(Primitives)
typescript
z.string() // 字符串
z.number() // 数字
z.bigint() // BigInt
z.boolean() // 布尔值
z.symbol() // Symbol
z.undefined() // undefined
z.null() // null
z.any() // 任意值
z.unknown() // 未知值
z.never() // 永不可能的值
z.void() // void
5.2 类型胁迫(Coercion)
使用 z.coerce 强制转换输入值为目标类型:
typescript
z.coerce.string() // 强制转为字符串
z.coerce.number() // 强制转为数字
z.coerce.boolean() // 强制转为布尔值
z.coerce.bigint() // 强制转为 BigInt
5.3 字面量(Literals)
typescript
z.literal("hello") // 精确匹配 "hello"
z.literal(5) // 精确匹配 5
z.literal(null) // 精确匹配 null
z.union([z.literal("a"), z.literal("b")]) // 匹配 "a" 或 "b"
5.4 对象(Object)
typescript
const Schema = z.object({
name: z.string(),
age: z.number().optional(), // 可选字段
email: z.string().default(""), // 默认值
id: z.string().nullable(), // 允许 null
});
// 提取 shape
type Shape = typeof Schema.shape;
5.5 数组(Array)
typescript
z.array(z.string()) // 字符串数组
z.string().array() // 等价写法
z.array(z.number()).min(1).max(10) // 长度限制
z.array(z.string()).nonempty() // 非空数组
5.6 高级类型
typescript
// 元组
z.tuple([z.string(), z.number()])
// 联合类型
z.union([z.string(), z.number()])
// 或使用简写
z.string().or(z.number())
// 交叉类型
z.intersection(z.object({ a: z.string() }), z.object({ b: z.number() }))
// 记录类型(类似 TS 的 Record)
z.record(z.string(), z.number())
// 枚举
z.enum(["Red", "Green", "Blue"])
// 或使用原生 enum
z.nativeEnum(Color)
5.7 字符串格式校验
Zod 内置了丰富的字符串格式验证:
typescript
z.string().email() // 邮箱格式
z.string().uuid() // UUID 格式
z.string().url() // URL 格式
z.string().httpUrl() // HTTP/HTTPS URL
z.string().hostname() // 主机名
z.string().emoji() // Emoji
z.string().base64() // Base64
z.string().ip() // IP 地址
z.string().datetime() // ISO 日期时间
z.string().date() // ISO 日期
z.string().time() // ISO 时间
z.string().duration() // ISO 持续时间
5.8 自定义校验(Refinements)
使用 .refine() 添加自定义校验逻辑:
typescript
const Schema = z.string().refine(
(val) => val.length >= 8,
{ message: "至少需要 8 个字符" }
);
// 更复杂的示例:密码必须包含数字
const PasswordSchema = z.string().refine(
(val) => /\d/.test(val),
"密码必须包含至少一个数字"
);
链式使用多个 refine:
typescript
const Schema = z.string()
.refine(val => val.length >= 8, "至少8个字符")
.refine(val => /\d/.test(val), "必须包含数字")
.refine(val => /[A-Z]/.test(val), "必须包含大写字母");
5.9 数据转换(Transformations)
使用 .transform() 在验证通过后转换数据:
typescript
const DateSchema = z.string().transform((str) => new Date(str));
const result = DateSchema.parse("2024-01-01");
// result 的类型是 Date,而非 string
六、高级用法
6.1 异步校验
对于异步操作(如数据库查询),使用 parseAsync 和 safeParseAsync:
typescript
const Schema = z.string().refine(async (val) => {
// 模拟异步检查,如查询数据库
return val.length <= 8;
});
await Schema.parseAsync("hello"); // 返回 "hello"
const result = await Schema.safeParseAsync("hello");
6.2 递归类型
typescript
type Category = {
name: string;
subcategories: Category[];
};
const CategorySchema: z.ZodType<Category> = z.lazy(() =>
z.object({
name: z.string(),
subcategories: z.array(CategorySchema),
})
);
6.3 条件模式(Discriminated Union)
typescript
const CircleSchema = z.object({
kind: z.literal("circle"),
radius: z.number(),
});
const SquareSchema = z.object({
kind: z.literal("square"),
sideLength: z.number(),
});
const ShapeSchema = z.discriminatedUnion("kind", [
CircleSchema,
SquareSchema,
]);
6.4 自定义错误消息
Schema 级别自定义:
typescript
const Schema = z.string({
error: "用户名必须是字符串",
}).min(3, { error: "用户名至少 3 个字符" });
全局错误自定义:
typescript
import { config } from "zod";
config({
customError: (issue) => {
return `验证失败:${issue.message}`;
}
});
6.5 国际化支持
Zod 4 内置了多语言支持:
typescript
import { locales } from "zod/v4/core";
// 使用特定语言环境
config({
locale: locales.zh_CN,
});
七、生态系统与集成
Zod 拥有丰富的生态系统,可与多种库无缝集成:
| 类别 | 工具/库 |
|---|---|
| API 框架 | tRPC、express-zod-api、Zodios |
| 表单验证 | React Hook Form、Formik |
| NestJS | @anatine/zod-nestjs |
| 文档生成 | zod-to-json-schema、zod-to-openapi |
| 类型工具 | zod-to-ts |
与 React Hook Form 集成示例:
typescript
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
const schema = z.object({
email: z.string().email(),
password: z.string().min(8),
});
const { register, handleSubmit } = useForm({
resolver: zodResolver(schema),
});
八、总结
| 维度 | 说明 |
|---|---|
| 本质 | TypeScript 优先的运行时数据校验库 |
| 核心价值 | 一份定义 → 编译时类型 + 运行时校验 |
| 适用场景 | API 数据校验、表单验证、配置文件校验、跨端数据校验 |
| 学习曲线 | 低------API 直观,与 TypeScript 类型系统高度一致 |
| 生产就绪 | 零依赖、轻量、不可变 API,广泛用于生产环境 |
Zod 是目前 TypeScript 生态中最流行的运行时校验方案,它优雅地弥补了 TypeScript 类型系统在运行时的空白,是构建类型安全应用的必备工具。