Zod 校验库

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 异步校验

对于异步操作(如数据库查询),使用 parseAsyncsafeParseAsync

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 类型系统在运行时的空白,是构建类型安全应用的必备工具。

相关推荐
guangzan7 个月前
AI → JSON → UI
ui·ai·zod
guangzan7 个月前
Zod:TypeScript 类型守卫与数据验证
ai·typescript·zod