版本控制
更新 — 2025 年 7 月 8 日
¥Update — July 8th, 2025
zod@4.0.0
已发布到 npm
。包根 ("zod"
) 现在导出 Zod 4。所有其他子路径均保持不变,并将永久可用。
¥zod@4.0.0
has been published to npm
. The package root ("zod"
) now exports Zod 4. All other subpaths have not changed and will remain available forever.
要升级到 Zod 4:
¥To upgrade to Zod 4:
如果你正在使用 Zod 4,你现有的导入("zod/v4"
和 "zod/v4-mini"
)将永远有效。但是,升级后,你可以选择按如下方式重写导入:
¥If you are using Zod 4, your existing imports ("zod/v4"
and "zod/v4-mini"
) will continue to work forever. However, after upgrading, you can optionally rewrite your imports as follows:
修改前 | 修改后 | |
---|---|---|
Zod 4 | "zod/v4" | "zod" |
Zod 4 迷你版 | "zod/v4-mini" | "zod/mini" |
Zod 3 | "zod" | "zod/v3" |
库作者 — 如果你已经根据 库作者 指南中概述的最佳实践实现了 Zod 4 支持,请将你的对等依赖提升至包含 zod@^4.0.0
:
¥Library authors — if you've already implemented Zod 4 support according to the best practices outlined in the Library authors guide, bump your peer dependency to include zod@^4.0.0
:
无需进行其他代码更改。最新的 3.25.x
版本和 4.0.0
之间没有进行任何代码更改。这不需要主要版本升级。
¥There should be no other code changes necessary. No code changes were made between the latest 3.25.x
release and 4.0.0
. This does not require a major version bump.
Some notes on subpath versioning
最终,子路径版本控制方案是强制生态系统以不破坏性的方式升级的必要之恶。如果我一开始就发布 zod@4.0.0
,大多数库都会直接修改它们的对等依赖,从而强制整个生态系统使用 "版本跳跃雪崩"。
¥Ultimately, the subpath versioning scheme was a necessary evil to force the ecosystem to upgrade in a non-breaking way. If I'd published zod@4.0.0
out of the gate, most libraries would have naively bumped their peer dependencies, forcing a "version bump avalanche" across the ecosystem.
目前,整个生态系统中都有适用于 Zod 4 的 广泛支持。没有哪个迁移过程是完全轻松的,但我担心的 "版本雪崩" 似乎并没有发生。总体而言,库已经能够同时支持 Zod 3 和 Zod 4:Hono、LangChain、React Hook Form 等。一些生态系统维护者特意联系我,告诉我逐步添加对 Zod 4 的支持是多么方便(这通常需要主要版本升级)。长话短说:这种方法效果很好!很少有其他库受到与 Zod 相同的限制,但我强烈建议其他拥有大型相关生态系统的库考虑采用类似的方法。
¥As it stands, there is now broad support for Zod 4 across the ecosystem. No migration process is totally painless, but it seems like the "version avalanche" I'd feared didn't happen. By and large, libraries have been able to support Zod 3 and Zod 4 simultaneously: Hono, LangChain, React Hook Form, etc. Several ecosystem maintainers reached out to me specifically to indicate how convenient it was to incrementally add support for Zod 4 (something that would typically require a major version bump). Long story short: this approach worked great! Few other libraries are subject to the same constraints as Zod, but I strongly encourage other libraries with large associated ecosystems to consider a similar approach.
Zod 4 中的版本控制
¥Versioning in Zod 4
本文介绍了 Zod 4 的版本控制方法,旨在帮助用户和 Zod 相关库生态系统更轻松地迁移到 Zod 4。
¥This is a writeup of Zod 4's approach to versioning, with the goal of making it easier for users and Zod's ecosystem of associated libraries to migrate to Zod 4.
通用方法:
¥The general approach:
-
Zod 4 最初不会在 npm 上以
zod@4.0.0
的形式发布。相反,它将与zod@3.25.0
一起导出到子路径 ("zod/v4"
)¥Zod 4 will not initially be published as
zod@4.0.0
on npm. Instead it will be exported at a subpath ("zod/v4"
) alongsidezod@3.25.0
-
尽管如此,Zod 4 仍然被认为是稳定的,并且可以投入生产。
¥Despite this, Zod 4 is considered stable and production-ready
-
Zod 3 将继续从包根目录 (
"zod"
) 导出,以及新的子路径"zod/v3"
。它将继续进行错误修复和稳定性改进。¥Zod 3 will continue to be exported from the package root (
"zod"
) as well as a new subpath"zod/v3"
. It will continue to receive bug fixes & stability improvements.
这种方法类似于 Golang 处理主要版本变更的方式:https://go.dev/doc/modules/major-version
¥This approach is analogous to how Golang handles major version changes: https://go.dev/doc/modules/major-version
稍后:
¥Sometime later:
-
包根 (
"zod"
) 将从导出 Zod 3 切换到 Zod 4。¥The package root (
"zod"
) will switch over from exporting Zod 3 to Zod 4 -
此时
zod@4.0.0
将发布到 npm。¥At this point
zod@4.0.0
will get published to npm -
"zod/v4"
子路径将永久可用¥The
"zod/v4"
subpath will remain available forever
为什么?
¥Why?
Zod 在生态系统中占据着独特的地位。生态系统中的许多库/框架都接受用户定义的 Zod 模式。这意味着它们面向用户的 API 与 Zod 及其各种类/接口/实用程序紧密耦合。对于这些库/框架,Zod 的重大变更必然会导致其用户也面临重大变更。Zod 3 ZodType
无法分配给 Zod 4 ZodType
。
¥Zod occupies a unique place in the ecosystem. Many libraries/frameworks in the ecosystem accept user-defined Zod schemas. This means their user-facing API is strongly coupled to Zod and its various classes/interfaces/utilities. For these libraries/frameworks, a breaking change to Zod necessarily causes a breaking change for their users. A Zod 3 ZodType
is not assignable to a Zod 4 ZodType
.
为什么库不能同时支持 v3 和 v4?
¥Why can't libraries just support v3 and v4 simultaneously?
遗憾的是,peerDependencies 的局限性(以及包管理器之间的不一致)使得同时优雅地支持一个库的两个主要版本变得极其困难。
¥Unfortunately the limitations of peerDependencies (and inconsistencies between package managers) make it extremely difficult to elegantly support two major versions of one library simultaneously.
如果我直接将 zod@4.0.0
发布到 npm,Zod 生态系统中的绝大多数库都需要发布一个新的主要版本才能正确支持 Zod 4,包括一些备受瞩目的库,例如 AI SDK。它会在整个生态系统中触发 "版本跳跃雪崩",通常会带来大量的挫败感和工作量。
¥If I naively published zod@4.0.0
to npm, the vast majority of the libraries in Zod's ecosystem would need to publish a new major version to properly support Zod 4, include some high-profile libraries like the AI SDK. It would trigger a "version bump avalanche" across the ecosystem and generally create a huge amount of frustration and work.
通过子路径版本控制,我们解决了这个问题。它为库提供了一种同时支持 Zod 3 和 Zod 4(包括 Zod Mini)的简单方法。它们可以继续在 "zod"
上定义单个 peerDependency;无需更晦涩难懂的解决方案,例如 npm 别名、可选的对等依赖、"zod-compat"
包或其他类似的 hack。
¥With subpath versioning, we solve this problem. it provides a straightforward way for libraries to support Zod 3 and Zod 4 (including Zod Mini) simultaneously. They can continue defining a single peerDependency on "zod"
; no need for more arcane solutions like npm aliases, optional peer dependencies, a "zod-compat"
package, or other such hacks.
库需要将其 "zod"
依赖的最低版本提升至 zod@^3.25.0
。然后,它们可以在实现中同时引用 Zod 3 和 Zod 4:
¥Libraries will need to bump the minimum version of their "zod"
peer dependency to zod@^3.25.0
. They can then reference both Zod 3 and Zod 4 in their implementation:
稍后,一旦 v4 获得广泛支持,我们将升级 npm
的主要版本,并开始从包根目录导出 Zod 4,从而完成转换。(现在已经发生了 - 请参阅本页顶部的注释。)
¥Later, once there's broad support for v4, we'll bump the major version on npm
and start exporting Zod 4 from the package root, completing the transition. (This has now happened—see the note at the top of this page.)
只要库只从关联的子路径(而非根路径)导入,它们的实现就能在主版本升级后继续工作,无需代码更改。
¥As long as libraries are importing exclusively from the associated subpaths (not the root), their implementations will continue to work across the major version bump without code changes.
虽然这看起来可能不太传统(至少对于不使用 Go 语言的人来说是这样),但据我所知,这是唯一一种能够为 Zod 用户和更广泛生态系统中的库提供干净、增量迁移路径的方法。
¥While it may seem unorthodox (at least for people who don't use Go!), this is the only approach I'm aware of that enables a clean, incremental migration path for both Zod's users and the libraries in the broader ecosystem.
深入探讨为什么在这种情况下同级依赖不起作用。
¥A deeper dive into why peer dependencies don't work in this situation.
假设你是一个库,尝试构建一个接受 Zod 模式的 acceptSchema
函数。你希望能够接受 Zod 3 或 Zod 4 模式。在这个假设中,我假设 Zod 4 在 npm 上以 zod@4
的名称发布,没有子路径。以下是你的选项:
¥Imagine you're a library trying to build a function acceptSchema
that accepts a Zod schema. You want to be able to accept Zod 3 or Zod 4 schemas. In this hypothetical, I'm imagine Zod 4 was published as zod@4
on npm, no subpaths. Here are your options:
-
使用 npm 别名同时将 zod@3 和 zod@4 安装为
dependencies
。此方法有效,但最终你需要包含 Zod 3 和 Zod 4 的副本。你无法保证用户的 Zod 模式与你从依赖中提取的 z.ZodType 类的实例相同(instanceof
检查可能会失败)。¥Install both zod@3 and zod@4 as
dependencies
simultaneously using npm aliases. This works but you end up including your own copies of both Zod 3 and Zod 4. You have no guarantee that your user's Zod schemas are instances of the same z.ZodType class you're pulling from dependencies (instanceof
checks will probably fail). -
使用跨多个主要版本的对等依赖:
"zod@>=3.0.0"
……但在开发库时,你仍然需要选择一个版本进行开发。通常,你会将其安装为开发依赖。你有责任确保你的代码在两个版本中都能逐个字符地正常工作。在 Zod 3 和 Zod 4 中,这是不可能的,因为许多非常基础的类都简化了/使用了不同的泛型。¥Use a peer dependency that spans multiple major versions:
"zod@>=3.0.0"
…but when developing a library you’d still need to pick a version to develop against. Usually you'd install this as a dev dependency. The onus is on you to painstakingly ensure your code works, character-for-character, across both versions. This is impossible in the case of Zod 3 & Zod 4 because a number of very fundamental classes have simplified/different generics. -
可选的对等依赖。我就是找不到一个直接的答案,关于如何在所有平台上可靠地确定运行时安装了哪个对等依赖。网上很多答案都会说是 "在 try/catch 中使用动态导入来检查包是否存在"。这些人假设你使用后端,因为任何前端打包工具都不支持后端。当你尝试打包未安装的依赖时,它们会失败。显然,如果你在构建步骤中处于 try/catch 语句中,则不会受到影响。此外:由于我们讨论的是同一个库的多个版本,因此你需要使用 npm 别名来区分
package.json
中的两个版本。最新的 npm 版本(例如 v10)无法处理对等依赖 + npm 别名的组合。¥Optional peer dependencies. i just couldn't find a straight answer about how to reliably determine which peer dep is installed at runtime across all platforms. Many answers online will say "use dynamic imports in a try/catch to check it a package exists". Those folks are assuming you're on the backend because no frontend bundlers have no affordance for this. They'll fail when you try to bundle a dependency that isn't installed. Obviuosly it doesn't matter if you're inside a try/catch during a build step. Also: since we're talking about multiple versions of the same library, you'd need to use npm aliases to differentiate the two versions in your
package.json
. Versions of npm as recent as v10 cannot handle the combination of peer dependencies + npm aliases. -
zod-compat
。你在网上看到的这个极其复杂的解决方案是 "为每个版本定义一些代表基本功能的接口"。基本上,一些实用类型库可以用来模拟真实的情况。这很容易出错,工作量巨大,需要与实际实现保持同步,而且最终库的开发是基于库的影子版本进行的,而影子版本可能缺乏细节。它也仅适用于以下类型:如果一个库依赖于 Zod 中的任何运行时代码,它就会崩溃。¥
zod-compat
. This extremely hand-wavy solution you see online is "define interfaces for each version that represents some basic functionality". Basically some utility types libraries can use to approximate the real deal. This is error prone, a ton of work, needs to be kept synchronized with the real implementations, and ultimately libraries are developing against a shadow version of your library that probably lacks detail. It also only works for types: if a library depends on any runtime code in Zod it falls apart.
因此,需要子路径。
¥Hence, subpaths.