上手Zod v4的schema验证:一份声明搞定unknown输入与类型推断
【免费下载链接】zodTypeScript-first schema validation with static type inference项目地址: https://gitcode.com/GitHub_Trending/zo/zod
API 把 number 字段返回成字符串 "100" 时,你写的 interface 救不了你——接口只在编译期生效,运行时数据照样裸奔。Zod 用一份 schema 解决这件事:声明一次数据结构,验证、错误定位、静态类型推断一次到位,核心包只有 2kb(gzip)。
🧱 先跑起来:安装 Zod 并完成第一次 parse
痛点一句话:每次收到外部数据(请求体、localStorage、配置文件)都要手写一堆typeof x === "number"检查。Zod 的思路是把这些检查收敛成一份声明,验证由库来跑。
先装包,它是零外部依赖的:
npm install zod当前版本是 4.5.4,Node 和现代浏览器都能直接用。下面这段演示最基础的用法:声明一个对象 schema,然后用.parse()验证一份数据:
import * as z from "zod"; const Player = z.object({ username: z.string(), xp: z.number(), }); const raw = { username: "billie", xp: 100 }; // 来自外部的 unknown 数据 const data = Player.parse(raw); // 不合规会直接抛 ZodError console.log(data.username); // "billie",且类型是 string运行后,parse成功会返回输入的强类型深拷贝;只要有任何字段不合 schema,它立即抛出一个ZodError中断流程。注意parse是"抛异常"风格的 API,适合"不合法就直接挂掉"的场景。
🩹 验证失败时怎么办:用 safeParse 定位到具体字段
如果你的代码不能因为一条脏数据就整体崩溃(比如要给用户回显表单错误),就换.safeParse():
const result = Player.safeParse({ username: 42, xp: "100" }); if (!result.success) { console.log(result.error.issues); // [{ code: "invalid_type", path: ["username"], message: "..." }, // { code: "invalid_type", path: ["xp"], message: "..." }] } else { const data = result.data; // 类型保证是 { username: string; xp: number } }结果是一个判别联合:success为 true 拿data,为 false 拿error.issues,每个 issue 都带path(错在哪个字段)和message,直接可以映射到表单红字上。
这里有个常见的版本坑:v4 的报错文案定制是直接把字符串传给 schema 的第一个参数,v3 时代的required_error/invalid_type_error参数不再适用;字符串校验也推荐用独立的z.email()而不是 v3 的z.string().email()写法:
const Signup = z.object({ email: z.email("请输入有效的邮箱"), // 定制文案 age: z.number().int("年龄必须是整数"), }); Signup.safeParse({ email: "abc", age: "18" }).error?.issues; // message 都会变成你写的中文文案🧬 别再单写 interface 了:z.infer 让类型跟 schema 走
痛点:同一份数据结构,你往往维护了两份真相——一份运行时校验,一份 TypeScript 类型,改字段时容易漏一处。Zod 的类型是从 schema 推导出来的,只有一份真相:
// schema 不变,类型自动推导 type Player = z.infer<typeof Player>; // => { username: string; xp: number } function render(p: Player) { return p.username; // 编辑器直接知道类型,无需手写 interface }当 schema 带.transform()或 codec 这类"输入类型 ≠ 输出类型"的构造时,还能分别取z.input<>和z.output<>,对应数据进和出的两个方向。想对照完整 API,可以翻 packages/docs/content/basics.mdx。
🔁 一份 schema 前后端共用:z.codec 双向转换
痛点:服务端存 Date,网络上传 ISO 字符串,客户端拿到后再转回来——两头各写一遍转换函数,方向一对不上就出 bug。z.codec()(4.1 引入)把"两个方向的 schema + 两个方向的转换"打包成一个可共用的对象:
下面这段演示一个典型的 ISO 字符串与 Date 互转 codec:
const stringToDate = z.codec( z.iso.datetime(), // 输入方向:ISO 时间字符串 z.date(), // 输出方向:Date 对象 { decode: (iso) => new Date(iso), // 字符串 -> Date encode: (d) => d.toISOString(), // Date -> 字符串 } ); stringToDate.decode("2024-01-15T10:30:00.000Z"); // => Date stringToDate.encode(new Date("2024-01-15T10:30:00.000Z")); // => ISO 字符串客户端用.decode()把网络数据转成富类型,服务端返回前用.encode()转回 JSON 友好格式,转换规则只写一次、天然不会跑偏。
🌲 业务里绕不开的两个结构:判别联合与递归 schema
真实的业务数据很少是平铺对象:要么是多分支联合,要么是无限嵌套。这两个 Zod 都有原生构造:
// 判别联合:用 kind 字段区分分支,类型推导也分得开 const Shape = z.discriminatedUnion("kind", [ z.object({ kind: z.literal("circle"), radius: z.number() }), z.object({ kind: z.literal("square"), side: z.number() }), ]); // 递归结构:用 z.lazy 让 schema 指向自己 const TreeNode = z.object({ value: z.string(), children: z.array(z.lazy(() => TreeNode)).optional(), });判别联合在运行时按kind精确分发,TreeNode能验证任意深度的树形数据;z.lazy是解决"schema 定义时引用自己还没定义完"这个循环问题的标准手段。
⚡ 热点路径嫌慢:z.compile 预编译出快路径
痛点:高频场景(比如每条请求都过一遍 schema)里,常规 parse 的逐节点分发是有成本的。z.compile()会对 schema 做一次 AOT 编译,生成带编译快路径的克隆:
import * as z from "zod"; const Player = z.object({ username: z.string(), xp: z.number() }); const Fast = z.compile(Player); Fast.parse({ username: "billie", xp: 100 }); // 合法输入走编译后的快路径官方 55 个 schema 的基准测试里,中位数提速 2.4 倍,schema 负载越重收益越大——大对象数组约 9 倍、20 字段对象约 9 倍;而裸z.string()这种几乎没东西可省的构造则没有收益。合法输入走快路径,非法输入自动回退常规解析器,所以错误报告与普通 parse 完全一致;另外只要只需要判断"合不合法",顶层的z.validate()比.safeParse().success最高快 16 倍。schema 类本身是怎么搭起来的(core 与 classic 层的继承关系),可以参考这张结构图和 packages/zod/src/v4/classic/ 源码:
下一步可以做什么
- 在你项目里找一个现有的
interface + 手写校验组合,把它替换成一个 Zod schema,用z.infer统一类型来源 - 打开 wiki/compile.md 和 packages/docs/content/compile.mdx,看
z.compile在异步 refinement 等场景下的回退细节 - 如果只想要轻量版本,试一下
import { z } from "zod/mini",API 风格略有不同但同样支持 codec 和 compile
【免费下载链接】zodTypeScript-first schema validation with static type inference项目地址: https://gitcode.com/GitHub_Trending/zo/zod
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考