上手Zod v4的schema验证:一份声明搞定unknown输入与类型推断
2026/8/31 8:10:12 网站建设 项目流程

上手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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询