迁移指南
本迁移指南旨在按影响程度从高到低的顺序列出 Zod 4 中的重大更改。要了解有关 Zod 4 的性能增强和新功能的更多信息,请阅读 介绍性文章。
¥This migration guide aims to list the breaking changes in Zod 4 in order of highest to lowest impact. To learn more about the performance enhancements and new features of Zod 4, read the introductory post.
为了给生态系统留出迁移时间,Zod 4 将首先与 Zod v3.25 一起发布。要使用 Zod 4,请升级到 zod@3.25.0
或更高版本:
¥To give the ecosystem time to migrate, Zod 4 will initially be published alongside Zod v3.25. To use Zod 4, upgrade to zod@3.25.0
or later:
Zod 4 可在 "/v4"
子路径下获取:
¥Zod 4 is available at the "/v4"
subpath:
注意 — Zod 3 导出了许多未记录的准内部工具类型和函数,这些类型和函数不被视为公共 API 的一部分。此处未记录对这些变量的更改。
¥Note — Zod 3 exported a number of undocumented quasi-internal utility types and functions that are not considered part of the public API. Changes to those are not documented here.
错误自定义
¥Error customization
Zod 4 将错误自定义 API 标准化,统一为单一的 error
参数。以前,Zod 的错误自定义 API 支离破碎且不一致。这在 Zod 4 中得到了清理。
¥Zod 4 standardizes the APIs for error customization under a single, unified error
param. Previously Zod's error customization APIs were fragmented and inconsistent. This is cleaned up in Zod 4.
弃用 message
¥deprecates message
将 message
替换为 error
。message
参数仍然受支持,但已弃用。
¥Replaces message
with error
. The message
parameter is still supported but deprecated.
删除 invalid_type_error
和 required_error
¥drops invalid_type_error
and required_error
invalid_type_error
/ required_error
参数已被删除。这些是几年前匆忙添加的,目的是自定义比 errorMap
更简洁的错误信息。它们带有各种各样的陷阱(它们不能与 errorMap
一起使用),并且与 Zod 的实际问题代码不一致(没有 required
问题代码)。
¥The invalid_type_error
/ required_error
params have been dropped. These were hastily added years ago as a way to customize errors that was less verbose than errorMap
. They came with all sorts of footguns (they can't be used in conjunction with errorMap
) and do not align with Zod's actual issue codes (there is no required
issue code).
现在可以使用新的 error
参数清晰地表示这些。
¥These can now be cleanly represented with the new error
parameter.
删除 errorMap
¥drops errorMap
已重命名为 error
。
¥This is renamed to error
.
错误映射现在还可以返回纯 string
(而不是 {message: string}
)。它们还可以返回 undefined
,告诉 Zod 将控制权交给链中的下一个错误映射。
¥Error maps can also now return a plain string
(instead of {message: string}
). They can also return undefined
, which tells Zod to yield control to the next error map in the chain.
ZodError
更新问题格式
¥updates issue formats
问题格式已显著简化。
¥The issue formats have been dramatically streamlined.
以下是 Zod 3 问题类型及其 Zod 4 等效类型的列表:
¥Below is the list of Zod 3 issues types and their Zod 4 equivalent:
虽然某些 Zod 4 问题类型已被合并、删除和修改,但每个问题在结构上仍然与 Zod 3 对应问题相似(在大多数情况下相同)。所有问题仍然遵循与 Zod 3 相同的基本接口,因此大多数常见的错误处理逻辑无需修改即可正常工作。
¥While certain Zod 4 issue types have been merged, dropped, and modified, each issue remains structurally similar to Zod 3 counterpart (identical, in most cases). All issues still conform to the same base interface as Zod 3, so most common error handling logic will work without modification.
更改错误映射优先级
¥changes error map precedence
错误映射的优先级已更改为更加一致。具体来说,传递给 .parse()
的错误映射不再优先于模式级错误映射。
¥The error map precedence has been changed to be more consistent. Specifically, an error map passed into .parse()
no longer takes precedence over a schema-level error map.
弃用 .format()
¥deprecates .format()
ZodError
上的 .format()
方法已被弃用。请使用顶层 z.treeifyError()
函数。阅读 格式错误文档 了解更多信息。
¥The .format()
method on ZodError
has been deprecated. Instead use the top-level z.treeifyError()
function. Read the Formatting errors docs for more information.
弃用 .flatten()
¥deprecates .flatten()
ZodError
上的 .flatten()
方法也已被弃用。请使用顶层 z.treeifyError()
函数。阅读 格式错误文档 了解更多信息。
¥The .flatten()
method on ZodError
has also been deprecated. Instead use the top-level z.treeifyError()
function. Read the Formatting errors docs for more information.
删除 .formErrors
¥drops .formErrors
此 API 与 .flatten()
相同。它的存在是由于历史原因,并且没有文档记录。
¥This API was identical to .flatten()
. It exists for historical reasons and isn't documented.
弃用 .addIssue()
和 .addIssues()
¥deprecates .addIssue()
and .addIssues()
如有必要,请直接推送到 err.issues
数组。
¥Directly push to err.issues
array instead, if necessary.
z.number()
不再使用无限值
¥no infinite values
POSITIVE_INFINITY
和 NEGATIVE_INFINITY
不再被视为 z.number()
的有效值。
¥POSITIVE_INFINITY
and NEGATIVE_INFINITY
are no longer considered valid values for z.number()
.
.safe()
不再接受浮点数
¥.safe()
no longer accepts floats
在 Zod 3 中,z.number().safe()
已弃用。它现在的行为与 .int()
完全相同(见下文)。重要的是,这意味着它不再接受浮点数。
¥In Zod 3, z.number().safe()
is deprecated. It now behaves identically to .int()
(see below). Importantly, that means it no longer accepts floats.
.int()
仅接受安全整数
¥.int()
accepts safe integers only
z.number().int()
API 不再接受不安全的整数(超出 Number.MIN_SAFE_INTEGER
和 Number.MAX_SAFE_INTEGER
的范围)。使用超出此范围的整数会导致自发舍入误差。(另:你应该切换到 z.int()
。)
¥The z.number().int()
API no longer accepts unsafe integers (outside the range of Number.MIN_SAFE_INTEGER
and Number.MAX_SAFE_INTEGER
). Using integers out of this range causes spontaneous rounding errors. (Also: You should switch to z.int()
.)
z.string()
更新
¥z.string()
updates
弃用 .email()
等
¥deprecates .email()
etc
字符串格式现在表示为 ZodString
的子类,而不是简单的内部细化。因此,这些 API 已被移至顶层 z
命名空间。顶层 API 也更简洁,更易于摇树优化。
¥String formats are now represented as subclasses of ZodString
, instead of simple internal refinements. As such, these APIs have been moved to the top-level z
namespace. Top-level APIs are also less verbose and more tree-shakable.
方法形式(z.string().email()
)仍然存在并像以前一样工作,但现在已被弃用。
¥The method forms (z.string().email()
) still exist and work as before, but are now deprecated.
.base64url()
不再使用填充
¥no padding in .base64url()
z.base64url()
(以前称为 z.string().base64url()
)中不再允许使用填充。通常,base64url 字符串最好是未填充的且 URL 安全的。
¥Padding is no longer allowed in z.base64url()
(formerly z.string().base64url()
). Generally it's desirable for base64url strings to be unpadded and URL-safe.
删除 z.string().ip()
¥drops z.string().ip()
此方法已被单独的 .ipv4()
和 .ipv6()
方法取代。如果需要同时接受两者,请使用 z.union()
将它们合并。
¥This has been replaced with separate .ipv4()
and .ipv6()
methods. Use z.union()
to combine them if you need to accept both.
更新 z.string().ipv6()
¥updates z.string().ipv6()
验证现在使用 new URL()
构造函数进行,这比旧的正则表达式方法更加健壮。一些之前通过验证的无效值现在可能会失败。
¥Validation now happens using the new URL()
constructor, which is far more robust than the old regular expression approach. Some invalid values that passed validation previously may now fail.
删除 z.string().cidr()
¥drops z.string().cidr()
同样,这已被单独的 .cidrv4()
和 .cidrv6()
方法取代。如果需要同时接受两者,请使用 z.union()
将它们合并。
¥Similarly, this has been replaced with separate .cidrv4()
and .cidrv6()
methods. Use z.union()
to combine them if you need to accept both.
z.coerce
更新
¥z.coerce
updates
所有强制布尔值的输入类型现在均为 unknown
。
¥The input type of all coerced booleans is now unknown
.
.default()
更新
¥.default()
updates
.default()
的应用发生了微妙的变化。如果输入是 undefined
,ZodDefault
将缩短解析过程并返回默认值。默认值必须可赋值给输出类型。
¥The application of .default()
has changed in a subtle way. If the input is undefined
, ZodDefault
short-circuits the parsing process and returns the default value. The default value must be assignable to the output type.
在 Zod 3 中,.default()
需要一个与输入类型匹配的值。ZodDefault
会解析默认值,而不是进行短路处理。因此,默认值必须可以赋值给 Schema 的输入类型。
¥In Zod 3, .default()
expected a value that matched the input type. ZodDefault
would parse the default value, instead of short-circuiting. As such, the default value must be assignable to the input type of the schema.
为了复制旧的行为,Zod 实现了一个新的 .prefault()
API。这是 "预解析默认值" 的缩写。
¥To replicate the old behavior, Zod implements a new .prefault()
API. This is short for "pre-parse default".
z.object()
ZodObject
类上的这些修饰符方法决定了模式如何处理未知键。在 Zod 4 中,此功能现在存在于顶层函数中。这更符合 Zod 的声明优先理念,并将所有对象变体置于平等地位。
¥These modifier methods on the ZodObject
class determine how the schema handles unknown keys. In Zod 4, this functionality now exists in top-level functions. This aligns better with Zod's declarative-first philosophy, and puts all object variants on equal footing.
弃用 .strict()
和 .passthrough()
¥deprecates .strict()
and .passthrough()
这些方法通常不再需要。请使用顶层 z.strictObject()
和 z.looseObject()
函数。
¥These methods are generally no longer necessary. Instead use the top-level z.strictObject()
and z.looseObject()
functions.
这些方法仍然可用,以实现向后兼容,并且不会被删除。它们被视为遗留问题。
¥These methods are still available for backwards compatibility, and they will not be removed. They are considered legacy.
弃用 .strip()
¥deprecates .strip()
这从来都不是特别有用,因为它是 z.object()
的默认行为。要将严格对象转换为 "regular" 对象,请使用 z.object(A.shape)
。
¥This was never particularly useful, as it was the default behavior of z.object()
. To convert a strict object to a "regular" one, use z.object(A.shape)
.
删除 .nonstrict()
¥drops .nonstrict()
这个长期弃用的 .strip()
别名已被删除。
¥This long-deprecated alias for .strip()
has been removed.
删除 .deepPartial()
¥drops .deepPartial()
此方法在 Zod 3 中早已弃用,现已在 Zod 4 中删除。此 API 没有直接的替代方案。在其实现中存在许多陷阱,其使用通常是一种反模式。
¥This has been long deprecated in Zod 3 and it now removed in Zod 4. There is no direct alternative to this API. There were lots of footguns in its implementation, and its use is generally an anti-pattern.
更改 z.unknown()
可选性
¥changes z.unknown()
optionality
在推断类型中,z.unknown()
和 z.any()
类型不再标记为 "可选键"。
¥The z.unknown()
and z.any()
types are no longer marked as "key optional" in the inferred types.
z.nativeEnum()
已弃用
¥z.nativeEnum()
deprecated
z.nativeEnum()
函数现已弃用,取而代之的是 z.enum()
。z.enum()
API 已重载,以支持类似枚举的输入。
¥The z.nativeEnum()
function is now deprecated in favor of just z.enum()
. The z.enum()
API has been overloaded to support an enum-like input.
作为 ZodEnum
重构的一部分,我们移除了一些长期弃用且冗余的功能。它们都是相同的,只是由于历史原因而存在。
¥As part of this refactor of ZodEnum
, a number of long-deprecated and redundant features have been removed. These were all identical and only existed for historical reasons.
z.array()
更改 .nonempty()
类型
¥changes .nonempty()
type
现在其行为与 z.array().min(1)
相同。推断类型保持不变。
¥This now behaves identically to z.array().min(1)
. The inferred type does not change.
现在,使用 z.tuple()
和 "rest" 参数可以更好地表示旧行为。这更接近 TypeScript 的类型系统。
¥The old behavior is now better represented with z.tuple()
and a "rest" argument. This aligns more closely to TypeScript's type system.
z.promise()
已弃用
¥z.promise()
deprecated
很少有理由使用 z.promise()
。如果你的输入可能是 Promise
,只需在使用 Zod 解析之前对其进行 await
处理即可。
¥There's rarely a reason to use z.promise()
. If you have an input that may be a Promise
, just await
it before parsing it with Zod.
如果你正在使用 z.promise
来定义与 z.function()
相关的异步函数,那么现在也无需再这样做了;请参阅下面的 ZodFunction
部分。
¥If you are using z.promise
to define an async function with z.function()
, that's no longer necessary either; see the ZodFunction
section below.
z.function()
z.function()
的结果不再是 Zod 模式。相反,它充当独立的 "函数工厂",用于定义 Zod 验证的函数。API 也发生了变化;你预先定义 input
和 output
模式,而不是使用 args()
和 .returns()
方法。
¥The result of z.function()
is no longer a Zod schema. Instead, it acts as a standalone "function factory" for defining Zod-validated functions. The API has also changed; you define an input
and output
schema upfront, instead of using args()
and .returns()
methods.
如果你迫切需要具有函数类型的 Zod 架构,请考虑使用 此解决方法。
¥If you have a desperate need for a Zod schema with a function type, consider this workaround.
添加 .implementAsync()
¥adds .implementAsync()
定义一个异步函数,请使用 implementAsync()
而不是 implement()
。
¥To define an async function, use implementAsync()
instead of implement()
.
.refine()
忽略类型谓词
¥ignores type predicates
在 Zod 3 中,将 类型谓词 作为细化函数传递仍然可以缩小模式的类型。这没有记录,但在一些问题中进行了讨论。不再支持此功能。
¥In Zod 3, passing a type predicate as a refinement functions could still narrow the type of a schema. This wasn't documented but was discussed in some issues. This is no longer the case.
删除 ctx.path
¥drops ctx.path
Zod 的新解析架构不会预求值 path
数组。这是一项必要的更改,它解锁了 Zod 4 的显著性能提升。
¥Zod's new parsing architecture does not eagerly evaluate the path
array. This was a necessary change that unlocks Zod 4's dramatic performance improvements.
删除函数作为第二个参数
¥drops function as second argument
已删除以下令人头疼的过载。
¥The following horrifying overload has been removed.
z.ostring()
等已删除
¥z.ostring()
, etc dropped
未记录的便捷方法 z.ostring()
、z.onumber()
等已被删除。这些是定义可选字符串模式的简写方法。
¥The undocumented convenience methods z.ostring()
, z.onumber()
, etc. have been removed. These were shorthand methods for defining optional string schemas.
z.literal()
删除 symbol
支持
¥drops symbol
support
符号不被视为字面值,也不能简单地与 ===
进行比较。这是 Zod 3 中的一个疏忽。
¥Symbols aren't considered literal values, nor can they be simply compared with ===
. This was an oversight in Zod 3.
删除静态 .create()
工厂函数
¥static .create()
factories dropped
以前,所有 Zod 类都定义了一个静态 .create()
方法。这些现在已实现为独立的工厂函数。
¥Previously all Zod classes defined a static .create()
method. These are now implemented as standalone factory functions.
z.record()
删除单个参数用法
¥drops single argument usage
之前,z.record()
可以与单个参数一起使用。不再支持此功能。
¥Before, z.record()
could be used with a single argument. This is no longer supported.
改进枚举支持
¥improves enum support
记录模式用于验证诸如 之类的类型。在 Zod 3 中,将枚举作为键模式传递给 z.record()
将导致部分类型。
¥Records have gotten a lot smarter. In Zod 3, passing an enum into z.record()
as a key schema would result in a partial type
在 Zod 4 中,情况不再如此。推断类型符合你的预期,Zod 确保其详尽无遗;也就是说,它会在解析过程中确保所有枚举键都存在于输入中。
¥In Zod 4, this is no longer the case. The inferred type is what you'd expect, and Zod ensures exhaustiveness; that is, it makes sure all enum keys exist in the input during parsing.
z.intersection()
合并冲突时抛出 Error
异常
¥throws Error
on merge conflict
Zod 交集会根据两个模式解析输入,然后尝试合并结果。在 Zod 3 中,当结果无法合并时,Zod 会抛出一个带有特殊 "invalid_intersection_types"
问题的 ZodError
错误。
¥Zod intersection parses the input against two schemas, then attempts to merge the results. In Zod 3, when the results were unmergable, Zod threw a ZodError
with a special "invalid_intersection_types"
issue.
在 Zod 4 中,这将改为抛出常规 Error
错误。存在无法合并的结果表明模式存在结构性问题:两个不兼容类型的交集。因此,常规错误比验证错误更合适。
¥In Zod 4, this will throw a regular Error
instead. The existence of unmergable results indicates a structural problem with the schema: an intersection of two incompatible types. Thus, a regular error is more appropriate than a validation error.
内部变更
¥Internal changes
Zod 的典型用户可能会忽略此行以下的所有内容。这些更改不会影响面向用户的 z
API。
¥The typical user of Zod can likely ignore everything below this line. These changes do not impact the user-facing z
APIs.
内部变更太多,无法在此一一列举,但其中一些变更可能与依赖某些实现细节(有意或无意)的普通用户相关。这些更改对于在 Zod 上构建工具的库作者来说尤其重要。
¥There are too many internal changes to list here, but some may be relevant to regular users who are (intentionally or not) relying on certain implementation details. These changes will be of particular interest to library authors building tools on top of Zod.
更新泛型
¥updates generics
多个类的泛型结构已更改。或许最重要的是对 ZodType
基类的更改:
¥The generic structure of several classes has changed. Perhaps most significant is the change to the ZodType
base class:
第二个泛型 Def
已被完全删除。现在,基类只跟踪 Output
和 Input
。以前 Input
的值默认为 Output
,现在默认为 unknown
。这使得涉及 z.ZodType
的泛型函数在许多情况下能够更直观地运行。
¥The second generic Def
has been entirely removed. Instead the base class now only tracks Output
and Input
. While previously the Input
value defaulted to Output
, it now defaults to unknown
. This allows generic functions involving z.ZodType
to behave more intuitively in many cases.
不再需要 z.ZodTypeAny
;请使用 z.ZodType
替代。
¥The need for z.ZodTypeAny
has been eliminated; just use z.ZodType
instead.
添加 z.core
¥adds z.core
许多实用函数和类型已移至新的 zod/v4/core
子包,以方便 zod/v4
和 zod/v4-mini
之间的代码共享。
¥Many utility functions and types have been moved to the new zod/v4/core
sub-package, to facilitate code sharing between zod/v4
and zod/v4-mini
.
为了方便起见,zod/v4/core
的内容也会从 zod/v4
和 zod/v4-mini
重新导出到 z.core
命名空间下。
¥For convenience, the contents of zod/v4/core
are also re-exported from zod/v4
and zod/v4-mini
under the z.core
namespace.
有关 Zod 核心 概念的更多信息,请参阅 TypeScript 手册。
¥Refer to the Zod Core docs for more information on the contents of the core sub-library.
移动 ._def
¥moves ._def
._def
属性现已移至 ._zod.def
。所有内部定义的结构都可能发生变化;这与库作者相关,但本文不会进行全面记录。
¥The ._def
property is now moved to ._zod.def
. The structure of all internal defs is subject to change; this is relevant to library authors but won't be comprehensively documented here.
删除 ZodEffects
¥drops ZodEffects
这不会影响面向用户的 API,但这是一个值得强调的内部更改。这是 Zod 处理细化方式的更大规模重构的一部分。
¥This doesn't affect the user-facing APIs, but it's an internal change worth highlighting. It's part of a larger restructure of how Zod handles refinements.
以前,改进和转换都位于一个名为 ZodEffects
的封装类中。这意味着将其中任何一个添加到模式中都会将原始模式封装在 ZodEffects
实例中。在 Zod 4 中,改进现在存在于模式本身中。更准确地说,每个模式都包含一个 "checks" 数组;"check" 的概念是 Zod 4 中的新概念,它概括了细化的概念,以包含可能具有副作用的转换,例如 z.toLowerCase()
。
¥Previously both refinements and transformations lived inside a wrapper class called ZodEffects
. That means adding either one to a schema would wrap the original schema in a ZodEffects
instance. In Zod 4, refinements now live inside the schemas themselves. More accurately, each schema contains an array of "checks"; the concept of a "check" is new in Zod 4 and generalizes the concept of a refinement to include potentially side-effectful transforms like z.toLowerCase()
.
这在 zod/v4-mini
API 中尤为明显,它严重依赖 .check()
方法来组合各种验证。
¥This is particularly apparent in the zod/v4-mini
API, which heavily relies on the .check()
method to compose various validations together.
添加 ZodTransform
¥adds ZodTransform
同时,转换操作已移至专用的 ZodTransform
类。此模式类表示输入转换;事实上,你现在可以定义独立的转换:
¥Meanwhile, transforms have been moved into a dedicated ZodTransform
class. This schema class represents an input transform; in fact, you can actually define standalone transformations now:
这主要与 ZodPipe
结合使用。.transform()
方法现在返回 ZodPipe
的一个实例。
¥This is primarily used in conjunction with ZodPipe
. The .transform()
method now returns an instance of ZodPipe
.
删除 ZodPreprocess
¥drops ZodPreprocess
与 .transform()
一样,z.preprocess()
函数现在返回一个 ZodPipe
实例,而不是专用的 ZodPreprocess
实例。
¥As with .transform()
, the z.preprocess()
function now returns a ZodPipe
instance instead of a dedicated ZodPreprocess
instance.
删除 ZodBranded
¥drops ZodBranded
现在,品牌化是通过直接修改推断类型来处理的,而不是使用专用的 ZodBranded
类。面向用户的 API 保持不变。
¥Branding is now handled with a direct modification to the inferred type, instead of a dedicated ZodBranded
class. The user-facing APIs remain the same.