发布说明
经过一年的积极开发:Zod 4 现已稳定发布!它更快、更轻、更符合 tsc 的优化,并且实现了一些长期请求的功能。
版本管理
升级命令:
关于所有重大变更的完整列表,请参阅 迁移指南。本文重点介绍新增功能和改进。
为什么发布新的主版本?
Zod v3.0 发布于 2021 年 5 月(!)。当时 Zod 在 GitHub 上只有 2700 颗星,并且每周下载量为 60 万。现在拥有 3.78 万星和 3100 万每周下载量(6 周前 beta 发布时是 2300 万!)。经过 24 个小版本之后,Zod 3 的代码库已经达到瓶颈;最常请求的功能和改进需要破坏兼容性的重大更改。
Zod 4 一举解决了 Zod 3 长期存在的设计限制,为多个长期开发的功能和性能飞跃铺平了道路。它关闭了 Zod 的 10 个最高票未解决 issue 中的 9 个。希望它能成为未来多年的新基础。
想快速浏览新增内容,请参阅目录,点击任意项目跳转相应章节。
性能基准测试
你可以在 Zod 仓库中自行运行以下基准测试:
然后运行指定基准:
字符串解析快 14 倍
数组解析快 7 倍
对象解析快 6.5 倍
此测试运行的是 Moltar 验证库基准。
tsc 实例数量降低 100 倍
考虑以下简单文件:
用 "zod/v3" 以及 tsc --extendedDiagnostics 编译该文件会产生超过 25000 次类型实例化。用 "zod/v4" 仅约 175 次。
Zod 仓库内有一个 tsc 基准测试游乐场。可以在 packages/tsc 使用编译器基准测试验证此效果。实现演变时具体数字可能变化。
更重要的是,Zod 4 重新设计简化了 ZodObject 及其它 schema 类的泛型,避免了某些棘手的“实例爆炸”问题。例如,重复链式调用 .extend() 和 .omit() —— 以前会导致编译器问题:
Zod 3 编译此代码耗时约 4000ms;继续添加 .extend() 会触发“可能无限”错误。Zod 4 编译耗时仅 400ms,提升了 10 倍。
搭配即将推出的 tsgo 编译器,Zod 4 的编辑器性能将在更大规模的 schema 和代码库中表现更佳。
核心包体积减半
考虑以下极简脚本:
这是验证中尽可能简单的例子,有助于衡量核心包体积 —— 即即使在简单用例中仍包含的代码。我们用 rollup 对比 Zod 3 和 Zod 4 打包结果。
| 包名 | 打包体积(gzip) |
|---|---|
| Zod 3 | 12.47kb |
| Zod 4 | 5.36kb |
Zod 4 中核心包约减少 57%(2.3 倍)。这很好,但还有更大的提升空间。
介绍 Zod Mini
Zod 的方法繁重 API 本质上难以做树摇优化。即使是我们的简单 z.boolean() 脚本,也会带入许多未使用的实现,比如 .optional()、.array() 等。写更精简的实现只能到此为止。此时 Zod Mini 问世。
它是符合 zod 功能一对一映射的变体,API 更函数式且支持树摇优化。Zod 采用链式方法,Zod Mini 通常用包装函数替代:
并不是所有的方法都消失了!Zod 和 Zod Mini 的解析方法是相同的:
还有一个通用 .check() 方法用以添加细化规则。
以下顶级细化在 Zod Mini 中可用。它们对应的 Zod 方法应该是相当自明的。
此更函数式 API 让打包工具更容易摇掉未使用 API。推荐绝大多数场景使用 zod/v4,但对包体积限制苛刻的项目应考虑 zod/v4-mini。
这个更实用的 API 使得打包工具更容易去除你不使用的 API。虽然常规的 Zod 仍然推荐用于大多数用例,但任何对包大小有不寻常严格限制的项目都应该考虑使用 Zod Mini。
核心包大小减少 6.6 倍
这是上面的脚本,已更新为使用 "zod/mini" 而不是 "zod"。
用 rollup 打包后的 gzip 体积是 1.88kb,相比 zod@3 核心包体积减少 85%(约 6.6 倍)。
| 包名 | 打包体积(gzip) |
|---|---|
| Zod 3 | 12.47kb |
| Zod 4 (regular) | 5.36kb |
| Zod 4 (mini) | 1.88kb |
更多信息请见专门的 zod/mini 文档页。完整 API 细节混入现有文档,每当 API 有差异处代码块均含 "Zod" 和 "Zod Mini" 选项卡。
元数据
Zod 4 引入了一个为 schema 添加强类型元数据的新系统。元数据不存于 schema 内部,而存于一个“schema 注册表”,将 schema 与类型化元数据关联。通过 z.registry() 创建注册表:
往注册表添加 schema:
或用 schema 的 .register() 方法更方便:
全局注册表
Zod 还导出一个全局注册表 z.globalRegistry,支持一些常见 JSON Schema 兼容的元数据:
.meta() 方法
方便添加 schema 到 z.globalRegistry,可用 .meta() 方法。
为兼容 Zod 3,.describe() 依然可用,但推荐使用 .meta():
JSON Schema 转换
Zod 4 原生支持通过 z.toJSONSchema() 转换为 JSON Schema。
z.globalRegistry 中的任何元数据都会自动包含在 JSON Schema 输出中。
详见 JSON Schema 文档 了解如何定制生成的 JSON Schema。
递归对象
这是一个意外收获。经过多年尝试解决这个问题,我终于找到了方法让 Zod 正确推断递归对象类型。定义递归类型:
还能表示互相递归类型:
与 Zod 3 递归类型写法不同,无需类型断言。生成的 schema 是普通的 ZodObject 实例,支持完整方法集:
文件类型模式
验证 File 实例:
国际化
Zod 4 引入全局翻译错误消息的 locales API。
请参阅自定义错误中的完整支持语言列表;该部分会在有新支持语言时及时更新。
错误美化打印
zod-validation-error 的流行表明用户对官方美化错误的 API 需求很大。如果你已经用它,可继续使用。
Zod 新增了顶级函数 z.prettifyError 将 ZodError 转为用户友好的格式化字符串。
其输出如下多行格式化字符串:
当前格式不可配置,未来可能调整。
顶级字符串格式
所有“字符串格式”(邮箱等)均升级为 z 模块的顶级函数,更简洁且支持树摇。对应的方法式 API(如 z.string().email())虽仍可用,但已弃用,将在下个主版本移除。
自定义邮箱正则
z.email() 支持自定义正则表达式。邮箱没有单一标准正则,不同应用可选择不同严格程度。为方便,Zod 导出常用正则:
模板字面量类型
Zod 4 实现了 z.templateLiteral()。模板字面量类型是 TypeScript 类型系统里一个大的新功能,以前无法表示。
所有可转字符串的 Zod schema 类型(字符串、字符串格式如 z.email()、数字、布尔、大整数、枚举、字面量、undefined/optional、null/nullable 和其它模板字面量)都存有内部正则表达式。z.templateLiteral 将它们串联为超正则,可正确支持格式验证(自定义细化除外)。
详见 模板字面量文档。
数字格式
已添加用于表示定宽整数和浮点类型的新数字“格式”。这些格式返回一个已添加适当包含性最小/最大约束的 ZodNumber 实例。
同样,以下 bigint 数值格式也已添加。这些整数类型超出了 JavaScript 中 number 能安全表示的范围,因此它们会返回一个带有适当包含性最小/最大约束的 ZodBigInt 实例。
字符串布尔(StringBool)
已有的 z.coerce.boolean() 非常简单:假值(false、undefined、null、0、""、NaN 等)转换为 false,真值转换为 true。
这仍是一个好用且与其它 z.coerce 接口一致的 API。但部分用户请求更复杂的“环境变量风格”布尔值转换。为此,Zod 4 引入了 z.stringbool():
可自定义真值和假值:
简化的错误自定义
大多数破坏性更改涉及 错误自定义 API。Zod 3 时代它们比较混乱,Zod 4 使设计更优雅,值得着重说明。
简言之,现在统一使用单个 error 参数替代以下 API:
将 message 替换为 error。(message 依然支持但已弃用)
将 invalid_type_error 和 required_error 替换为函数形式的 error:
将 errorMap 替换为函数形式的 error:
升级的 z.discriminatedUnion()
判别联合现在支持了先前不支持的多种 schema 类型,包括联合和管道(pipe):
更重要的是,判别联合现在支持复合 — 可以在一个判别联合成员里再使用判别联合。
z.literal() 支持传入多个值
z.literal() 现在可选接受多个值。
Zod 3 版本写法:
细化仍存于 schemas 内部
Zod 3 时,细化存于一个包裹原有 schema 的 ZodEffects 类里,造成不能同时链式调用 .refine() 与 .min() 等方法:
Zod 4 时,细化内嵌于 schema 本身,上面代码可正常工作:
.overwrite()
.transform() 极其有用,但有个缺点:输出类型无法在运行时“可 introspect”,转换函数是黑盒,可返回任意值。这意味着很多功能(例如 JSON Schema 转换)无法正确支持。
Zod 4 新增 .overwrite() 表示“不会修改推断类型的转换”,返回原始类实例。覆盖函数存作细化,不改变推断类型。
现有 .trim()、.toLowerCase() 和 .toUpperCase() 方法已改用 .overwrite() 实现。
可扩展基础:zod/v4/core
此功能对大多数用户无关,但值得强调。Zod Mini 的添加催生了共享子包 zod/v4/core,包含 Zod 和 Zod Mini 共享的核心功能。
起初我抗拒设立这个,但如今认为这是 Zod 4 最重要特性之一,使 Zod 从简单库变成可供其它库灵活集成的高性能验证“底座”。
如果你在构建 Schema 库,请参考 Zod 和 Zod Mini 的实现,了解如何基于 zod/v4/core 构建。欢迎通过 GitHub 讨论或 X/Bluesky 联系我以获取帮助或反馈。
总结
我计划发布多篇解释诸如 zod/v4-mini 之类重大功能设计思想的系列文章,届时会更新本节。
我计划写一系列额外的帖子,解释一些主要功能(如 Zod Mini)背后的设计过程。随着这些帖子发布,我会更新这一部分。
对于库的作者,现在有一个专门的 库作者指南,描述了在 Zod 上构建的最佳实践。它回答了关于如何同时支持 Zod 3 和 Zod 4(包括 Mini)的常见问题。
祝你解析愉快!
— Colin McDonnell @colinhacks

