定义 Schema
要验证数据,你必须首先定义一个 Schema(模式)。Schema 代表 类型,从简单的原始值到复杂的嵌套对象和数组。
原始类型 (Primitives)
强制类型转换 (Coercion)
要将输入数据强制转换为适当的类型,请改用 z.coerce:
这些 Schema 的强制转换变体尝试将输入值转换为适当的类型。
默认情况下,这些强制转换 schema 的输入类型是 unknown。要指定更具体的输入类型,请传递一个泛型参数:
字面量 (Literals)
字面量 schema 代表一个 字面量类型,例如 "hello world" 或 5。
要表示 JavaScript 字面量 null 和 undefined:
允许及个字面量值:
从字面量 schema 中提取允许值的集合:
字符串 (Strings)
Zod 提供了少量内置的字符串验证和转换 API。执行一些常见的字符串验证:
执行一些简单的字符串转换:
字符串格式 (String formats)
要针对一些常见的字符串格式进行验证:
电子邮件 (Emails)
要验证电子邮件地址:
默认情况下,Zod 使用相对严格的电子邮件正则表达式,旨在验证包含常用字符的普通电子邮件地址。它大致相当于 Gmail 强制执行的规则。要了解有关此正则表达式的更多信息,请参阅 这篇文章。
要自定义电子邮件验证行为,可以将自定义正则表达式传递给 pattern 参数。
Zod 导出了几个你可以使用的有用正则表达式。
UUID
要验证 UUID:
要指定特定的 UUID 版本:
RFC 9562/4122 UUID 规范要求第 8 个字节的前两位为 10。其他类似 UUID 的标识符不强制执行此约束。要验证任何类似 UUID 的标识符:
URL
要验证任何兼容 WHATWG 的 URL:
如你所见,这是非常宽松的。在内部,它使用 new URL() 构造函数来验证输入;此行为可能因平台和运行时而异,但它是验证任何给定 JS 运行时/引擎上的 URI/URL 的最严格的方法。
要针对特定正则表达式验证主机名:
要针对特定正则表达式验证协议,请使用 protocol 参数。
Web URL — 在许多情况下,你会希望专门验证 Web URL。这是推荐的 schema:
这限制协议为 http/https,并确保主机名是带有 z.regexes.domain 正则表达式的有效域名:
To normalize URLs, use the normalize flag. This will overwrite the input value with the normalized URL returned by new URL().
ISO 日期时间 (ISO datetimes)
你可能已经注意到,Zod string 包含一些日期/时间相关的验证。这些验证是基于正则表达式的,因此不如完整的日期/时间库严格。但是,它们对于验证用户输入非常方便。
z.iso.datetime() 方法强制执行 ISO 8601;默认情况下,不允许时区偏移:
允许时区偏移:
允许非限定(无时区)的日期时间:
限制允许的时间 precision(精度)。默认情况下,秒是可选的,并且允许任意的亚秒精度。
ISO 日期 (ISO dates)
z.iso.date() 方法验证格式为 YYYY-MM-DD 的字符串。
ISO 时间 (ISO times)
z.iso.time() 方法验证格式为 HH:MM[:SS[.s+]] 的字符串。默认情况下,秒是可选的,亚秒小数也是如此。
不允许任何形式的偏移。
使用 precision 参数来限制允许的小数精度。
IP 地址 (IP addresses)
IP 块 (CIDR)
验证使用 CIDR 表示法 指定的 IP 地址范围。
MAC 地址 (MAC Addresses)
验证标准 48 位 MAC 地址 IEEE 802。
JWT
验证 JSON Web Tokens。
哈希 (Hashes)
要验证加密哈希值:
默认情况下,z.hash() 期望十六进制编码,这是惯例。你可以使用 enc 参数指定不同的编码:
自定义格式 (Custom formats)
要定义你自己的字符串格式:
此 schema 将产生 "invalid_format" 问题,这比 refinements 或 z.custom() 产生的 "custom" 错误更具描述性。
模板字面量 (Template literals)
新功能 — 在 [email protected] 中引入。
要定义模板字面量 schema:
z.templateLiteral API 可以处理任意数量的字符串字面量(例如 "hello")和 schema。任何推断类型可分配给 string | number | bigint | boolean | null | undefined 的 schema 都可以传递。
数字 (Numbers)
使用 z.number() 验证数字。它允许任何有限数字。
Zod 实现了几个针对数字的验证:
如果(出于某种原因)你想验证 NaN,请使用 z.nan()。
整数 (Integers)
要验证整数:
BigInts
要验证 BigInts:
Zod 包含一些针对 bigint 的验证。
布尔值 (Booleans)
要验证布尔值:
日期 (Dates)
使用 z.date() 验证 Date 实例。
要自定义错误消息:
Zod 提供了少量针对日期的验证。
枚举 (Enums)
使用 z.enum 根据一组固定的允许 字符串 值来验证输入。
支持类枚举的对象字面量 ({ [key: string]: string | number })。
你也可以传入外部声明的 TypeScript 枚举。
Zod 4 — 这替代了 Zod 3 中的 z.nativeEnum() API。
注意:不推荐 使用 TypeScript 的 enum 关键字。
.enum
要将 schema 的值提取为类枚举对象:
.exclude()
创建一个新的枚举 schema,排除某些值:
.extract()
创建一个新的枚举 schema,提取某些值:
Stringbools
💎 Zod 4 新功能
在某些情况下(例如解析环境变量),将某些字符串 "boolish"(类似布尔值)值解析为纯 boolean 值很有价值。为了支持这一点,Zod 4 引入了 z.stringbool():
要自定义 truthy 和 falsy 值:
默认情况下,该 schema 是 不区分大小写 的;所有输入在与 truthy/falsy 值比较之前都会转换为小写。要使其区分大小写:
可选类型 (Optionals)
使 schema 变为 可选(即允许 undefined 输入)。
这将返回一个包装原始 schema 的 ZodOptional 实例。要提取内部 schema:
可空类型 (Nullables)
使 schema 变为 可空(即允许 null 输入)。
这将返回一个包装原始 schema 的 ZodNullable 实例。要提取内部 schema:
Nullish
使 schema 变为 nullish(既可选又可空):
请参阅 TypeScript 手册以了解有关的概念 nullish。
Unknown
Zod 旨在与 TypeScript 的类型系统一一对应。因此,Zod 提供了 API 来表示以下特殊类型:
Never
没有值可以通过验证。
对象 (Objects)
要定义对象类型:
默认情况下,所有属性都是必需的。要使某些属性可选:
默认情况下,无法识别的键会从解析结果中 剥离:
z.strictObject
定义 严格 schema,当发现未知键时抛出错误:
z.looseObject
定义 宽松 schema,允许未知键通过:
.catchall()
定义 catchall schema,用于验证所有无法识别的键:
.shape
访问内部 schema:
.keyof()
从对象 schema 的键创建 ZodEnum schema:
.extend()
向对象 schema 添加字段:
此 API 可用于覆盖现有字段!小心使用这种能力!如果两个 schema 共享键,B 将覆盖 A。
替代方案:展开语法 — 你也可以完全避免使用 .extend(),而是创建一个全新的对象 schema。这使得结果 schema 的严格级别在视觉上更加明显。
你也可以使用它一次合并多个对象。
这种方法有几个优点:
- 它使用语言级功能(展开语法)而不是特定于库的 API
- 相同的语法在 Zod 和 Zod Mini 中都有效
tsc效率更高 —.extend()方法在大型 schema 上可能很昂贵,并且由于 TypeScript 限制 当调用被链接时它会变得更加昂贵- 如果你愿意,你可以通过使用
z.strictObject()或z.looseObject()更改结果 schema 的严格级别
.safeExtend()
.safeExtend() 方法的工作方式类似于 .extend(),但它不允许你用不可分配的 schema 覆盖现有属性。换句话说,.safeExtend() 的结果将具有 extends 原始类型(在 TypeScript 意义上)的推断类型。
使用 .safeExtend() 扩展包含 refinements 的 schema。(常规 .extend() 在用于具有 refinements 的 schema 时会抛出错误。)
.pick()
受 TypeScript 内置的 Pick 和 Omit 实用程序类型的启发,Zod 提供了专用的 API 来从对象 schema 中选择和省略某些键。
从这个初始 schema 开始:
选择某些键:
.omit()
省略某些键:
.partial()
为了方便起见,Zod 提供了一个专用的 API 来使某些或所有属性可选,灵感来自内置的 TypeScript 实用程序类型 Partial。
使所有字段可选:
使某些属性可选:
.required()
Zod 提供了一个 API 来使某些或所有属性为 必需,灵感来自 TypeScript 的 Required 实用程序类型。
使所有属性为必需:
使某些属性为必需:
递归对象 (Recursive objects)
要定义自引用类型,请在键上使用 getter。这允许 JavaScript在运行时解析循环 schema。
虽然支持递归 schema,但将循环数据传递给 Zod 会导致无限循环。
你也可以表示 相互递归类型:
所有对象 API (.pick(), .omit(), .required(), .partial() 等) 都如预期的那样工作。
循环错误 (Circularity errors)
由于 TypeScript 的限制,递归类型推断可能很棘手,并且仅在某些情况下有效。一些更复杂的类型可能会触发如下递归类型错误:
在这种情况下,你可以使用有问题的 getter 上的类型注释来解决错误:
数组 (Arrays)
要定义数组 schema:
要访问数组元素的内部 schema:
Zod 实现了许多针对数组的验证:
元组 (Tuples)
与数组不同,元组通常是固定长度的数组,为每个索引指定不同的 schema。
要添加可变参数("rest"):
联合 (Unions)
联合类型 (A | B) 表示逻辑“或”。Zod 联合 schema 将按顺序根据每个选项检查输入。返回第一个验证成功的值。
要提取内部选项 schema:
可辨识联合 (Discriminated unions)
可辨识联合 是一种特殊的联合,其中 a) 所有选项都是对象 schema,b) 共享一个特定的键(“鉴别器”或 discriminator)。根据鉴别器键的值,TypeScript 能够像你预期的那样“缩小”类型签名。
你可以用常规的 z.union() 来表示它。但常规联合是 朴素的——它们按顺序根据每个选项检查输入,并返回第一个通过的选项。对于大型联合来说,这可能很慢。
因此,Zod 提供了一个 z.discriminatedUnion() API,它使用 鉴别器键 来使解析更高效。
每个选项都应该是一个 对象 schema,其鉴别器属性(上例中的 status)对应于某个字面量值或值集,通常是 z.enum()、z.literal()、z.null() 或 z.undefined()。
交叉类型 (Intersections)
交叉类型 (A & B) 表示逻辑“与”。
这对于交叉两个对象类型很有用。
合并对象 schema 时,首选 A.extend(B) 而不是交叉。使用 .extend() 会给你一个新的对象 schema,而 z.intersection(A, B) 返回一个 ZodIntersection 实例,它缺少常见的对象方法,如 pick 和 omit。
记录 (Records)
Record schema 用于验证诸如 Record<string, string> 之类的类型。
Key schema 可以是任何可分配给 string | number | symbol 的 Zod schema。
要创建包含由枚举定义的键的对象 schema:
Zod 4 — 在 Zod 4 中,如果你将 z.enum 作为第一个参数传递给 z.record(),Zod 将全面检查所有枚举值是否作为键存在于输入中。这种行为与 TypeScript 一致:
在 Zod 3 中,不检查完整性。要复制旧行为,请使用 z.partialRecord()。
如果你想要一个 partial(部分)记录类型,请使用 z.partialRecord()。这将跳过 Zod 通常使用 z.enum() 和 z.literal() 键 schema 运行的特殊完整性检查。
Maps
Sets
Set schema 可以使用以下实用程序方法进一步约束。
文件 (Files)
要验证 File 实例:
Promises
已弃用 — z.promise() 在 Zod 4 中已弃用。Promise schema 的有效用例极少。如果你怀疑某个值可能是 Promise,只需在用 Zod 解析之前 await 它。
Instanceof
你可以使用 z.instanceof 来检查输入是否为类的实例。这对于针对从第三方库导出的类验证输入通过很有用。
属性 (Property)
要针对 Zod schema 验证类实例的特定属性:
z.property() API 适用于任何数据类型(但在与 z.instanceof() 结合使用时最有用)。
Refinements
每个 Zod schema 都存储一个 refinements 数组。Refinements 是一种执行 Zod 没有为其提供原生 API 的自定义验证的方法。
.refine()
Refinement 函数不应该抛出错误。相反,它们应该返回一个 falsy 值来表示失败。Zod 不会捕获抛出的错误。
error
要自定义错误消息:
abort
默认情况下,来自 check 的验证问题被认为是 可继续的;也就是说,即使其中一个导致验证错误,Zod 也会按顺序执行 所有 check。这通常是可取的,因为这意味着 Zod 可以一次性显示尽可能多的错误。
要将特定的 refinement 标记为 不可继续,请使用 abort 参数。如果 check 失败,验证将终止。
path
要自定义错误路径,请使用 path 参数。这通常仅在对象 schema 的上下文中有用。
这将在相关 issue 中设置 path 参数:
要定义异步 refinement,只需传递一个 async 函数:
如果你使用异步 refinements,你必须使用 .parseAsync 方法来解析数据!否则 Zod 会抛出错误。
when
注意 — 这是一个高级用户功能,如果在 refinements 内部滥用可能会增加未捕获错误的概率。
默认情况下,如果已经遇到任何 不可继续 的问题,refinements 将不会运行。Zod 在将其传递给任何 refinement 函数之前,会仔细确保值的类型签名正确。
在某些情况下,你希望更精细地控制 refinements 的运行时间。例如,考虑此“密码确认”检查:
anotherField 上的错误将阻止密码确认检查执行,即使该检查不依赖于 anotherField。要控制 refinement 何时运行,请使用 when 参数:
.superRefine()
常规 .refine API 仅生成具有 "custom" 错误代码的单个 issue,但 .superRefine() 可以使用任何 Zod 内部 issue 类型 创建多个 issue。
.check()
注意 — .check() API 是一个更底层的 API,通常比 .superRefine() 更复杂。在性能敏感的代码路径中它可能更快,但它也更冗长。
编解码器 (Codecs)
新功能 — 在 Zod 4.1 中引入。有关更多信息,请参阅专用的 Codecs 页面。
编解码器(Codecs)是一种特殊的 schema,它实现了另外两个 schema 之间的 双向转换。
常规的 .parse() 操作执行 正向转换。它调用编解码器的 decode 函数。
或者,你可以使用顶级 z.decode() 函数。与 .parse()(接受 unknown 输入)不同,z.decode() 期望强类型输入(在此示例中为 string)。
要执行 反向转换,请使用逆过程:z.encode()。
有关更多信息,请参阅专用的 Codecs 页面。该页面包含常用编解码器的实现,你可以将其复制/粘贴到你的项目中:
stringToNumberstringToIntstringToBigIntnumberToBigIntisoDatetimeToDateepochSecondsToDateepochMillisToDatejsonCodecutf8ToBytesbytesToUtf8base64ToBytesbase64urlToByteshexToBytesstringToURLstringToHttpURLuriComponentstringToBoolean
管道 (Pipes)
Schema 可以链接在一起形成“管道(pipes)”。当管道与 Transforms 结合使用时尤其有用。
变换 (Transforms)
注意 — 对于双向变换,请使用 codecs。
Transforms 是一种特殊的 schema,它执行单向转换。它们接受任何内容并对数据执行某些转换,而不是验证输入。要定义一个 transform:
Transform 函数不应该抛出错误。Zod 不会捕获抛出的错误。
要在 transform 内部执行验证逻辑,请使用 ctx。要报告验证问题,请将新问题推送到 ctx.issues(类似于 .check() API)。
通常,transforms 与 Pipes 结合使用。这种组合对于执行一些初始验证,然后将解析后的数据转换为另一种形式非常有用。
.transform()
将某些 schema 传输到 transform 中是一种常见的模式,因此 Zod 提供了一个便捷的 .transform() 方法。
Transforms 也可以是异步的:
如果你使用异步 transforms,你在解析数据时必须使用 .parseAsync 或 .safeParseAsync!否则 Zod 会抛出错误。
.preprocess()
将 transform 传输到另一个 schema 是另一种常见的模式,因此 Zod 提供了一个便捷的 z.preprocess() 函数。
默认值 (Defaults)
要为 schema 设置默认值:
或者,你可以传递一个函数,该函数将在需要生成默认值时重新执行:
预设默认 (Prefaults)
在 Zod 中,设置 default 值将使解析过程短路。如果输入是 undefined,则会立即返回默认值。因此,默认值必须可分配给 schema 的 输出类型。
有时,定义一个 prefault(“预解析默认”)值很有用。如果输入是 undefined,则会解析 prefault 值。解析过程 不会 短路。因此,prefault 值必须可分配给 schema 的 输入类型。
如果你想通过某些变异的 refinements 传递某些输入值,这也很有用。
Catch
使用 .catch() 定义在验证错误时返回的回退值:
或者,你可以传递一个函数,该函数将在需要生成 catch 值时重新执行。
品牌类型 (Branded types)
TypeScript 的类型系统是 结构化 的,这意味着两个结构等价的类型被认为是相同的。
在某些情况下,可能需要在 TypeScript 内部模拟 名义类型。这可以通过 品牌类型(branded types,也称为“不透明类型”)来实现。
在底层,这是通过将“品牌”附加到 schema 的推断类型来实现的。
有了这个品牌,任何普通的(无品牌)数据结构都不能再分配给推断类型。你必须使用 schema 解析一些数据才能获得品牌数据。
注意,品牌类型不影响 .parse 的运行时结果。它只是一个静态构造。
Readonly
要将 schema 标记为只读:
新 schema 的推断类型将被标记为 readonly。请注意,在 TypeScript 中,这仅影响对象、数组、元组、Set 和 Map:
输入将像往常一样解析,然后结果将使用 Object.freeze() 冻结,以防止修改。
JSON
要验证任何可 JSON 编码的值:
这是一个方便的 API,它返回以下联合 schema:
函数 (Functions)
Zod 提供了一个 z.function() 实用程序来定义 Zod 验证的函数。这样,你可以避免将验证代码与业务逻辑混合。
函数 schema 有一个 .implement() 方法,该方法接受一个函数并返回一个自动验证其输入和输出的新函数。
如果输入无效,此函数将抛出 ZodError:
如果你只关心验证输入,可以省略 output 字段。
使用 .implementAsync() 方法创建异步函数。
自定义 (Custom)
你可以使用 z.custom() 为任何 TypeScript 类型创建 Zod schema。这对于为 Zod 开箱即用不支持的类型创建 schema 非常有用,例如模板字符串字面量。
如果你不提供验证函数,Zod 将允许任何值。这可能很危险!
你可以通过传递第二个参数来自定义错误消息和其他选项。此参数的工作方式与 .refine 的 params 参数相同。

