
5 分钟用 Zod 搭起数据验证从 schema 到真实项目【免费下载链接】zodTypeScript-first schema validation with static type inference项目地址: https://gitcode.com/GitHub_Trending/zo/zod订单接口收到一条畸形 JSON页面直接白屏排查半天才发现某个字段是null而不是字符串。这类问题可以在入口处统一拦截用 Zod 定义 schema再对进来的数据做数据验证。先弄明白 Zod 是什么一张表看清定位Zod 是一个 TypeScript 优先的 schema 库用声明式语法描述数据形状运行时校验输入同时静态推断出对应的 TypeScript 类型。它不绑定任何框架校验逻辑写在哪层都能复用。维度说明体积核心包 gzip 后约 2KB见 README.md依赖零运行时依赖类型推断z.infertypeof schema静态推导类型不用手写 interface不可变.min()、.default()等方法都返回新 schema原定义不会被修改安装 Zod写第一个 schema下面用 npm 安装yarn、pnpm 同理npm install zod第一个 schema 用来拦住「评分必须是 1~5 的数字」这种最基本的形状import { z } from zod; const 评分 z.number().min(1).max(5); 评分.parse(4); // 合法原样返回 4 评分.parse(五星); // 非法抛出 ZodErrorparse和safeParse都能完成校验区别只在于失败时的行为parsesafeParse合法数据返回处理后的值success: true附data非法数据抛ZodError需 try/catch返回success: false附error适合场景内部信任的数据用户输入、第三方接口这段演示了safeParse的返回结构失败时不会中断流程const 结果 评分.safeParse(五星); if (!结果.success) { console.log(结果.error.issues); // [{ code: too_small, ... }] }按数据形状组织校验标量、对象、数组、组合类型官方文档目录在 packages/docs/content/其中 schema 各层的关系可以参考这张结构图标量字段是最常用的校验入口。以产品评论字段为例把类型约束集中在一处声明const 评论 z.object({ 评分: z.number().int().min(1).max(5), 内容: z.string().min(5).max(200), 昵称: z.string().optional(), 头像: z.string().url().nullable(), });对象场景下z.infer可以把 schema 直接翻译成类型后续函数签名不用再手写一遍 interfacetype 评论类型 z.infertypeof 评论; // { 评分: number; 内容: string; 昵称?: string; 头像: string | null }数组用z.array包一层即可长度约束同样可以链式追加const 订单行 z.array( z.object({ 商品编码: z.string(), 数量: z.number().int().min(1) }), ).min(1).max(50);组合类型处理取值不确定但有边界的情况下面三种最常见const 订单编号 z.union([z.string(), z.number()]); // 二选一 const 订单状态 z.enum([待支付, 已支付, 已取消]); // 封闭集合 const 带备注 z.intersection(评论, z.object({ 备注: z.string() })); // 叠加当校验失败时用 safeParse 接住错误映射回表单字段校验失败时safeParse返回的error.issues是关键。每个 issue 带code、path、message其中path精确到字段数组下标也在里面这正是把错误挂回表单的依据function 收集错误(error: z.ZodError) { const 字段错误: Recordstring, string {}; for (const i of error.issues) { const key i.path.join(.); // 如 items.0.数量 if (!字段错误[key]) 字段错误[key] i.message; } return 字段错误; }这段把上面的收集错误接到safeParse上表单里按 key 渲染提示即可const 结果 评论.safeParse({ 评分: 5, 内容: 好, 头像: abc }); if (!结果.success) { const errs 收集错误(结果.error); // { 内容: Too small: expected string to have 5 characters, 头像: Invalid url } }落到项目里表单提交与接口出入参两件事表单提交refine 管住跨字段规则。单个字段的规则写在 schema 里跨字段规则比如两次密码一致用refine补上提交时一次性收集全部错误const 注册表单 z.object({ 用户名: z.string().min(3).max(20), 邮箱: z.string().email(), 密码: z.string().min(8), 确认密码: z.string(), }).refine((d) d.密码 d.确认密码, { message: 两次密码不一致, path: [确认密码], });接口出入参default 兜底、coerce 转类型。对第三方返回逐字段校验后再使用对 URL query 参数用z.coerce把字符串转成目标类型const 订单列表响应 z.object({ 订单: z.array(订单行), 下一页游标: z.string().nullable().default(null), }); const 页大小 z.coerce.number().int().min(1).max(100).default(20); const 响应 订单列表响应.parse(await res.json()); const size 页大小.parse(new URLSearchParams(location.search).get(size));注意coerce的代价z.coerce.number().parse(abc)得到NaN后才会报invalid_type而不是不是数字。对来源不可信的数据显式判断比隐式强转更清晰。踩坑与效率技巧默认值写在 schema 里。用.default()声明兜底值而不是在业务代码里|| 兜底类型和运行时行为才一致。refine之后别继续期待类型变化。它不改变输出类型建议放在链的末尾需要转换时用transform。safeParse失败后看issues别只看message。message是整串拼接文本逐字段定位要靠issues的path。z.infer对输入类型和输出类型区分。用了default/transform后z.input与z.output会不同需要区分时用这两个工具类型。接着看什么官方文档源文件packages/docs/content/从 basics.mdx 开始真实用例参考测试目录packages/zod/src/v4/classic/tests/schema 核心实现packages/zod/src/v4/core/下一步建议挑你项目里最不稳定的一处接口返回先用safeParse包一层把issues打印出来看一周再决定哪些字段值得收紧。【免费下载链接】zodTypeScript-first schema validation with static type inference项目地址: https://gitcode.com/GitHub_Trending/zo/zod创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考