Zod logo

定义模式

要验证数据,必须首先定义一个模式。Schema 表示各种类型,从简单的原始值到复杂的嵌套对象和数组。

¥To validate data, you must first define a schema. Schemas represent types, from simple primitive values to complex nested objects and arrays.

原语

¥Primitives

import { z } from "zod/v4";
 
// primitive types
z.string();
z.number();
z.bigint();
z.boolean();
z.symbol();
z.undefined();
z.null();

强制转换

¥Coercion

要将输入数据强制转换为适当的类型,请改用 z.coerce

¥To coerce input data to the appropriate type, use z.coerce instead:

z.coerce.string();    // String(input)
z.coerce.number();    // Number(input)
z.coerce.boolean();   // Boolean(input)
z.coerce.bigint();    // BigInt(input)

这些模式的强制类型会尝试将输入值转换为适当的类型。

¥The coerced variant of these schemas attempts to convert the input value to the appropriate type.

const schema = z.coerce.string();
 
schema.parse("tuna");    // => "tuna"
schema.parse(42);        // => "42"
schema.parse(true);      // => "true"
schema.parse(null);      // => "null"

字面量

¥Literals

字面量模式代表 字面类型,例如 "hello world"5

¥Literal schemas represent a literal type, like "hello world" or 5.

const tuna = z.literal("tuna");
const twelve = z.literal(12);
const twobig = z.literal(2n);
const tru = z.literal(true);
const terrific = z.literal(Symbol("terrific"));

要表示 JavaScript 字面量 nullundefined

¥To represent the JavaScript literals null and undefined:

z.null();
z.undefined();
z.void(); // equivalent to z.undefined()

要允许多个字面量值:

¥To allow multiple literal values:

const colors = z.literal(["red", "green", "blue"]);
 
colors.parse("green"); // ✅
colors.parse("yellow"); // ❌

要从字面模式中提取允许值的集合:

¥To extract the set of allowed values from a literal schema:

colors.values; // => Set<"red" | "green" | "blue">

字符串

¥Strings

Zod 提供了一些内置的字符串验证和转换 API。要执行一些常见的字符串验证:

¥Zod provides a handful of built-in string validation and transform APIs. To perform some common string validations:

z.string().max(5);
z.string().min(5);
z.string().length(5);
z.string().regex(/^[a-z]+$/);
z.string().startsWith("aaa");
z.string().endsWith("zzz");
z.string().includes("---");
z.string().uppercase();
z.string().lowercase();

要执行一些简单的字符串转换:

¥To perform some simple string transforms:

z.string().trim(); // trim whitespace
z.string().toLowerCase(); // toLowerCase
z.string().toUpperCase(); // toUpperCase

字符串格式

¥String formats

要针对某些常见字符串格式进行验证:

¥To validate against some common string formats:

z.email();
z.uuid();
z.url();
z.emoji();         // validates a single emoji character
z.base64();
z.base64url();
z.nanoid();
z.cuid();
z.cuid2();
z.ulid();
z.ipv4();
z.ipv6();
z.cidrv4();        // ipv4 CIDR block
z.cidrv6();        // ipv6 CIDR block
z.iso.date();
z.iso.time();
z.iso.datetime();
z.iso.duration();

电子邮件

¥Emails

要验证电子邮件地址:

¥To validate email addresses:

z.email();

默认情况下,Zod 使用一个相对严格的电子邮件正则表达式,用于验证包含常见字符的普通电子邮件地址。它大致相当于 Gmail 强制执行的规则。要了解有关此正则表达式的更多信息,请参阅 此文章

¥By default, Zod uses a comparatively strict email regex designed to validate normal email addresses containing common characters. It's roughly equivalent to the rules enforced by Gmail. To learn more about this regex, refer to this post.

/^(?!\.)(?!.*\.\.)([a-z0-9_'+\-\.]*)[a-z0-9_+-]@([a-z0-9][a-z0-9\-]*\.)+[a-z]{2,}$/i

要自定义电子邮件验证行为,可以将自定义正则表达式传递给 pattern 参数。

¥To customize the email validation behavior, you can pass a custom regular expression to the pattern param.

z.email({ pattern: /your regex here/ });

Zod 导出了几个你可以使用的有用的正则表达式。

¥Zod exports several useful regexes you could use.

// Zod's default email regex
z.email();
z.email({ pattern: z.regexes.email }); // equivalent
 
// the regex used by browsers to validate input[type=email] fields
// https://web.nodejs.cn/en-US/docs/Web/HTML/Element/input/email
z.email({ pattern: z.regexes.html5Email });
 
// the classic emailregex.com regex (RFC 5322)
z.email({ pattern: z.regexes.rfc5322Email });
 
// a loose regex that allows Unicode (good for intl emails)
z.email({ pattern: z.regexes.unicodeEmail });

UUIDs

要验证 UUID:

¥To validate UUIDs:

z.uuid();

要指定特定的 UUID 版本:

¥To specify a particular UUID version:

// supports "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8"
z.uuid({ version: "v4" });
 
// for convenience
z.uuidv4();
z.uuidv6();
z.uuidv7();

RFC 4122 UUID 规范要求字节 8 的前两位为 10。其他类似 UUID 的标识符不强制执行此约束。要验证任何类似 UUID 的标识符:

¥The RFC 4122 UUID spec requires the first two bits of byte 8 to be 10. Other UUID-like identifiers do not enforce this constraint. To validate any UUID-like identifier:

z.guid();

URLs

要验证任何与 WHATWG 兼容的 URL。

¥To validate any WHATWG-compatible URL.

const schema = z.url();
 
schema.parse("https://example.com"); // ✅
schema.parse("http://localhost"); // ✅
 
schema.parse("mailto:noreply@zod.dev"); // ❌
schema.parse("sup"); // ❌

在内部,这使用 new URL() 构造函数来执行验证。这在不同平台和运行时中可能表现不同,但通常是验证 URI/URL 最严格的方法。

¥Internally this uses the new URL() constructor to perform validation. This may behave differently across platforms and runtimes but is generally the most rigorous way to validate URIs/URLs.

要根据特定的正则表达式验证主机名:

¥To validate the hostname against a specific regex:

const schema = z.url({ hostname: /^example\.com$/ });
 
schema.parse("https://example.com"); // ✅
schema.parse("https://zombo.com"); // ❌

要根据特定的正则表达式验证协议,请使用 protocol 参数。

¥To validate the protocol against a specific regex, use the protocol param.

const schema = z.url({ protocol: /^https$/ });
 
schema.parse("https://example.com"); // ✅
schema.parse("http://example.com"); // ❌

ISO 日期时间

¥ISO datetimes

你可能已经注意到,Zod 字符串包含一些与日期/时间相关的验证。这些验证基于正则表达式,因此它们不像完整的日期/时间库那样严格。但是,它们对于验证用户输入非常方便。

¥As you may have noticed, Zod string includes a few date/time related validations. These validations are regular expression based, so they are not as strict as a full date/time library. However, they are very convenient for validating user input.

z.iso.datetime() 方法强制执行 ISO 8601 标准;默认情况下,不允许使用时区偏移:

¥The z.iso.datetime() method enforces ISO 8601; by default, no timezone offsets are allowed:

const datetime = z.iso.datetime();
 
datetime.parse("2020-01-01T00:00:00Z"); // ✅
datetime.parse("2020-01-01T00:00:00.123Z"); // ✅
datetime.parse("2020-01-01T00:00:00.123456Z"); // ✅ (arbitrary precision)
datetime.parse("2020-01-01T00:00:00+02:00"); // ❌ (no offsets allowed)

要允许时区偏移:

¥To allow timezone offsets:

const datetime = z.iso.datetime({ offset: true });
 
datetime.parse("2020-01-01T00:00:00+02:00"); // ✅
datetime.parse("2020-01-01T00:00:00.123+02:00"); // ✅ (millis optional)
datetime.parse("2020-01-01T00:00:00.123+0200"); // ✅ (millis optional)
datetime.parse("2020-01-01T00:00:00.123+02"); // ✅ (only offset hours)
datetime.parse("2020-01-01T00:00:00Z"); // ✅ (Z still supported)

要允许非限定(无时区)日期时间:

¥To allow unqualified (timezone-less) datetimes:

const schema = z.iso.datetime({ local: true });
schema.parse("2020-01-01T00:00:00"); // ✅

要限制允许的 precision(默认情况下,支持任意亚秒精度)。

¥To constrain the allowable precision (by default, arbitrary sub-second precision is supported).

const datetime = z.iso.datetime({ precision: 3 });
 
datetime.parse("2020-01-01T00:00:00.123Z"); // ✅
datetime.parse("2020-01-01T00:00:00Z"); // ❌
datetime.parse("2020-01-01T00:00:00.123456Z"); // ❌

ISO 日期

¥ISO dates

z.iso.date() 方法验证 YYYY-MM-DD 格式的字符串。

¥The z.iso.date() method validates strings in the format YYYY-MM-DD.

const date = z.iso.date();
 
date.parse("2020-01-01"); // ✅
date.parse("2020-1-1"); // ❌
date.parse("2020-01-32"); // ❌

ISO 时间

¥ISO times

Zod 3.23 新增

¥Added in Zod 3.23

z.iso.time() 方法验证 HH:MM:SS[.s+] 格式的字符串。第二个参数可以包含任意小数精度。它不允许任何类型的时区偏移。

¥The z.iso.time() method validates strings in the format HH:MM:SS[.s+]. The second can include arbitrary decimal precision. It does not allow timezone offsets of any kind.

const time = z.iso.time();
 
time.parse("00:00:00"); // ✅
time.parse("09:52:31"); // ✅
time.parse("23:59:59.9999999"); // ✅ (arbitrary precision)
 
time.parse("00:00:00.123Z"); // ❌ (no `Z` allowed)
time.parse("00:00:00.123+02:00"); // ❌ (no offsets allowed)

你可以设置 precision 选项来限制允许的小数精度。

¥You can set the precision option to constrain the allowable decimal precision.

const time = z.iso.time({ precision: 3 });
 
time.parse("00:00:00.123"); // ✅
time.parse("00:00:00.123456"); // ❌
time.parse("00:00:00"); // ❌

IP 地址

¥IP addresses

const ipv4 = z.ipv4();
v4.parse("192.168.0.0"); // ✅
 
const ipv6 = z.ipv6();
v6.parse("2001:db8:85a3::8a2e:370:7334"); // ✅

IP 地址块 (CIDR)

¥IP blocks (CIDR)

验证使用 CIDR 表示法 指定的 IP 地址范围。

¥Validate IP address ranges specified with CIDR notation.

const cidrv4 = z.string().cidrv4();
cidrv4.parse("192.168.0.0/24"); // ✅
 
const cidrv6 = z.string().cidrv6();
cidrv6.parse("2001:db8::/32"); // ✅

数字

¥Numbers

使用 z.number() 验证数字。它允许任意有限数字。

¥Use z.number() to validate numbers. It allows any finite number.

const schema = z.number();
 
schema.parse(3.14);      // ✅
schema.parse(NaN);       // ❌
schema.parse(Infinity);  // ❌

Zod 实现了一些特定于数字的验证:

¥Zod implements a handful of number-specific validations:

z.number().gt(5);
z.number().gte(5);                     // alias .min(5)
z.number().lt(5);
z.number().lte(5);                     // alias .max(5)
z.number().positive();       
z.number().nonnegative();    
z.number().negative(); 
z.number().nonpositive(); 
z.number().multipleOf(5);              // alias .step(5)

如果(出于某种原因)你想要验证 NaN,请使用 z.nan()

¥If (for some reason) you want to validate NaN, use z.nan().

z.nan().parse(NaN);              // ✅
z.nan().parse("anything else");  // ❌

整数

¥Integers

要验证整数:

¥To validate integers:

z.int();     // restricts to safe integer range
z.int32();   // restrict to int32 range

BigInts

要验证 BigInt:

¥To validate BigInts:

z.bigint();

Zod 包含一些特定于 bigint 的验证。

¥Zod includes a handful of bigint-specific validations.

z.bigint().gt(5n);
z.bigint().gte(5n);                    // alias `.min(5n)`
z.bigint().lt(5n);
z.bigint().lte(5n);                    // alias `.max(5n)`
z.bigint().positive(); 
z.bigint().nonnegative(); 
z.bigint().negative(); 
z.bigint().nonpositive(); 
z.bigint().multipleOf(5n);             // alias `.step(5n)`

布尔值

¥Booleans

要验证布尔值:

¥To validate boolean values:

z.boolean().parse(true); // => true
z.boolean().parse(false); // => false

日期

¥Dates

使用 z.date() 验证 Date 实例。

¥Use z.date() to validate Date instances.

z.date().safeParse(new Date()); // success: true
z.date().safeParse("2022-01-12T00:00:00.000Z"); // success: false

要自定义错误消息:

¥To customize the error message:

z.date({
  error: issue => issue.input === undefined ? "Required" : "Invalid date"
});

Zod 提供了一些特定于日期的验证。

¥Zod provides a handful of date-specific validations.

z.date().min(new Date("1900-01-01"), { error: "Too old!" });
z.date().max(new Date(), { error: "Too young!" });

枚举

¥Enums

使用 z.enum 根据一组固定的允许字符串值验证输入。

¥Use z.enum to validate inputs against a fixed set of allowable string values.

const FishEnum = z.enum(["Salmon", "Tuna", "Trout"]);
 
FishEnum.parse("Salmon"); // => "Salmon"
FishEnum.parse("Swordfish"); // => ❌

注意 - 如果将字符串数组声明为变量,Zod 将无法正确推断每个元素的确切值。

¥Careful — If you declare your string array as a variable, Zod won't be able to properly infer the exact values of each element.

const fish = ["Salmon", "Tuna", "Trout"];
 
const FishEnum = z.enum(fish);
type FishEnum = z.infer<typeof FishEnum>; // string

要解决此问题,请始终将数组直接传递给 z.enum() 函数,或使用 as const

¥To fix this, always pass the array directly into the z.enum() function, or use as const.

const fish = ["Salmon", "Tuna", "Trout"] as const;
 
const FishEnum = z.enum(fish);
type FishEnum = z.infer<typeof FishEnum>; // "Salmon" | "Tuna" | "Trout"

你还可以传入外部声明的 TypeScript 枚举。

¥You can also pass in an externally-declared TypeScript enum.

Zod 4 — 这将取代 Zod 3 中的 z.nativeEnum() API。

¥Zod 4 — This replaces the z.nativeEnum() API in Zod 3.

请注意,使用 TypeScript 的 enum 关键字会得到 不推荐

¥Note that using TypeScript's enum keyword is not recommended.

enum Fish {
  Salmon = "Salmon",
  Tuna = "Tuna",
  Trout = "Trout",
}
 
const FishEnum = z.enum(Fish);

.enum

要将模式的值提取为类似枚举的对象:

¥To extract the schema's values as an enum-like object:

const FishEnum = z.enum(["Salmon", "Tuna", "Trout"]);
 
FishEnum.enum;
// => { Salmon: "Salmon", Tuna: "Tuna", Trout: "Trout" }

.exclude()

要创建新的枚举架构并排除某些值:

¥To create a new enum schema, excluding certain values:

const FishEnum = z.enum(["Salmon", "Tuna", "Trout"]);
const TunaOnly = FishEnum.exclude(["Salmon", "Trout"]);

.extract()

要创建新的枚举架构并提取某些值:

¥To create a new enum schema, extracting certain values:

const FishEnum = z.enum(["Salmon", "Tuna", "Trout"]);
const SalmonAndTroutOnly = FishEnum.extract(["Salmon", "Trout"]);

Stringbool

💎 Zod 4 中的新功能

¥💎 New in Zod 4

在某些情况下(例如解析环境变量),将某些字符串 "boolish" 值解析为普通的 boolean 值很有价值。为了支持这一点,Zod 4 引入了 z.stringbool()

¥In some cases (e.g. parsing environment variables) it's valuable to parse certain string "boolish" values to a plain boolean value. To support this, Zod 4 introduces z.stringbool():

const strbool = z.stringbool();
 
strbool.parse("true")         // => true
strbool.parse("1")            // => true
strbool.parse("yes")          // => true
strbool.parse("on")           // => true
strbool.parse("y")            // => true
strbool.parse("enable")       // => true
 
strbool.parse("false");       // => false
strbool.parse("0");           // => false
strbool.parse("no");          // => false
strbool.parse("off");         // => false
strbool.parse("n");           // => false
strbool.parse("disabled");    // => false
 
strbool.parse(/* anything else */); // ZodError<[{ code: "invalid_value" }]>

自定义真值和假值:

¥To customize the truthy and falsy values:

z.stringbool({
  truthy: ["yes", "true"],
  falsy: ["no", "false"]
})

默认情况下,Schema 不区分大小写;所有输入在与 truthy/falsy 值进行比较之前都会转换为小写。要使其区分大小写:

¥Be default the schema is case-insensitive; all inputs are converted to lowercase before comparison to the truthy/falsy values. To make it case-sensitive:

z.stringbool({
  case: "sensitive"
});

可选值

¥Optionals

要使架构可选(即允许 undefined 输入)。

¥To make a schema optional (that is, to allow undefined inputs).

z.optional(z.literal("yoda")); // or z.literal("yoda").optional()

这将返回一个封装原始模式的 ZodOptional 实例。要提取内部模式:

¥This returns a ZodOptional instance that wraps the original schema. To extract the inner schema:

optionalYoda.unwrap(); // ZodLiteral<"yoda">

可空值

¥Nullables

要使架构可空(即允许 null 输入)。

¥To make a schema nullable (that is, to allow null inputs).

z.nullable(z.literal("yoda")); // or z.literal("yoda").nullable()

这将返回一个封装原始模式的 ZodNullable 实例。要提取内部模式:

¥This returns a ZodNullable instance that wraps the original schema. To extract the inner schema:

nullableYoda.unwrap(); // ZodLiteral<"yoda">

将空值转换为空值

¥Nullish

要使架构可空(可选且可空):

¥To make a schema nullish (both optional and nullable):

const nullishYoda = z.nullish(z.literal("yoda"));

记录变得更加智能。

¥Refer to the TypeScript manual for more about the concept of nullish.

未知

¥Unknown

Zod 旨在一对一地镜像 TypeScript 的类型系统。因此,Zod 提供了 API 来表示以下特殊类型:

¥Zod aims to mirror TypeScript's type system one-to-one. As such, Zod provides APIs to represent the following special types:

// allows any values
z.any(); // inferred type: `any`
z.unknown(); // inferred type: `unknown`

从不

¥Never

任何值都无法通过验证。

¥No value will pass validation.

z.never(); // inferred type: `never`

模板字面量

¥Template literals

💎 Zod 4 中的新功能

¥💎 New in Zod 4

Zod 4 终于实现了 TypeScript 类型系统中最后一个尚未实现的功能:模板字面量。几乎所有原始模式都可以在 z.templateLiteral 中使用:字符串、类似 z.email() 的字符串格式、数字、布尔值、枚举、字面量(非模板类型)、可选/可空以及其他模板字面量。

¥Zod 4 finally implements one of the last remaining unrepresented features of TypeScript's type system: template literals. Virtually all primitive schemas can be used in z.templateLiteral: strings, string formats like z.email(), numbers, booleans, enums, literals (of the non-template variety), optional/nullable, and other template literals.

const hello = z.templateLiteral(["hello, ", z.string()]);
// `hello, ${string}`
 
const cssUnits = z.enum(["px", "em", "rem", "%"]);
const css = z.templateLiteral([z.number(), cssUnits ]);
// `${number}px` | `${number}em` | `${number}rem` | `${number}%`
 
const email = z.templateLiteral([
  z.string().min(1),
  "@",
  z.string().max(64),
]);
// `${string}@${string}` (the min/max refinements are enforced!)

对象

¥Objects

定义一个对象类型:

¥To define an object type:

  // all properties are required by default
  const Person = z.object({
    name: z.string(),
    age: z.number(),
  });
 
  type Person = z.infer<typeof Person>;
  // => { name: string; age: number; }

默认情况下,所有属性都是必需的。要使某些属性可选:

¥By default, all properties are required. To make certain properties optional:

const Dog = z.object({
  name: z.string(),
  age: z.number().optional(),
});
 
Dog.parse({ name: "Yeller" }); // ✅

默认情况下,无法识别的键将从解析结果中剥离:

¥By default, unrecognized keys are stripped from the parsed result:

Dog.parse({ name: "Yeller", extraKey: true });
// => { name: "Yeller" }

z.strictObject

定义一个在发现未知键时抛出错误的严格模式:

¥To define a strict schema that throws an error when unknown keys are found:

const StrictDog = z.strictObject({
  name: z.string(),
});
 
StrictDog.parse({ name: "Yeller", extraKey: true });
// ❌ throws

z.looseObject

定义一个允许未知键通过的宽松模式:

¥To define a loose schema that allows unknown keys to pass through:

const LooseDog = z.looseObject({
  name: z.string(),
});
 
Dog.parse({ name: "Yeller", extraKey: true });
// => { name: "Yeller", extraKey: true }

.catchall()

定义一个用于验证任何无法识别的键的通用模式:

¥To defina a catchall schema that will be used to validate any unrecognized keys:

const DogWithStrings = z.object({
  name: z.string(),
  age: z.number().optional(),
}).catchall(z.string());

DogWithStrings.parse({ name: "Yeller", extraKey: "extraValue" }); // ✅
DogWithStrings.parse({ name: "Yeller", extraKey: 42 }); // ❌

.shape

访问内部模式:

¥To access the internal schemas:

Dog.shape.name; // => string schema
Dog.shape.age; // => number schema

.keyof()

要从对象架构的键创建 ZodEnum 架构:

¥To create a ZodEnum schema from the keys of an object schema:

const keySchema = Dog.keyof();
// => ZodEnum<["name", "age"]>

.extend()

要向对象架构添加其他字段:

¥To add additional fields to an object schema:

const DogWithBreed = Dog.extend({
  breed: z.string(),
});

此 API 可用于覆盖现有字段!小心使用这种能力!

¥This API can be used to overwrite existing fields! Be careful with this power!

如果两个模式共享密钥,则 B 将覆盖 A。

¥If the two schemas share keys, B will override A.

.pick()

受 TypeScript 内置 PickOmit 工具类型的启发,Zod 提供了专用 API,用于从对象模式中选择和省略某些键。

¥Inspired by TypeScript's built-in Pick and Omit utility types, Zod provides dedicated APIs for picking and omitting certain keys from an object schema.

从此初始模式开始:

¥Starting from this initial schema:

const Recipe = z.object({
  name: z.string(),
  description: z.string().optional(),
  ingredients: z.array(z.string()),
});
// { id: string; name: string; ingredients: string[] }

要选择某些键:

¥To pick certain keys:

const JustTheTitle = Recipe.pick({ title: true });

.omit()

要省略某些键:

¥To omit certain keys:

const RecipeNoId = Recipe.omit({ id: true });

.partial()

为了方便起见,Zod 提供了一个专用 API,用于将部分或全部属性设为可选属性,其灵感来自内置的 TypeScript 工具类型 Partial

¥For convenience, Zod provides a dedicated API for making some or all properties optional, inspired by the built-in TypeScript utility type Partial.

要使所有字段可选:

¥To make all fields optional:

const PartialRecipe = Recipe.partial();
// { title?: string | undefined; description?: string | undefined; ingredients?: string[] | undefined }

要使某些属性可选:

¥To make certain properties optional:

const RecipeOptionalIngredients = Recipe.partial({
  ingredients: true,
});
// { title: string; description?: string | undefined; ingredients?: string[] | undefined }

.required()

Zod 提供了一个 API,用于将部分或所有属性设为必需,这受到了 TypeScript 的 Required 工具类型的启发。

¥Zod provides an API for making some or all properties required, inspired by TypeScript's Required utility type.

要使所有属性必填:

¥To make all properties required:

const RequiredRecipe = Recipe.required();
// { title: string; description: string; ingredients: string[] }

要使某些属性成为必需属性:

¥To make certain properties required:

const RecipeRequiredDescription = Recipe.required({description: true});
// { title: string; description: string; ingredients: string[] }

递归对象

¥Recursive objects

定义一个自引用类型,在键上使用 getter。这允许 JavaScript 在运行时解析循环模式。

¥To define a self-referential type, use a getter on the key. This lets JavaScript resolve the cyclical schema at runtime.

const Category = z.object({
  name: z.string(),
  get subcategories(){
    return z.array(Category)
  }
});
 
type Category = z.infer<typeof Category>;
// { name: string; subcategories: Category[] }

虽然支持递归 schema,但将循环数据传递给 Zod 会导致无限循环。

¥Though recursive schemas are supported, passing cyclical data into Zod will cause an infinite loop.

你还可以表示相互递归的类型:

¥You can also represent mutually recursive types:

const User = z.object({
  email: z.email(),
  get posts(){
    return z.array(Post)
  }
});
 
const Post = z.object({
  title: z.string(),
  get author(){
    return User
  }
});

所有对象 API(.pick().omit().required().partial() 等)均按预期工作。

¥All object APIs (.pick(), .omit(), .required(), .partial(), etc.) work as you'd expect.

由于 TypeScript 的限制,递归类型推断可能比较复杂,并且仅在特定场景下有效。一些更复杂的类型可能会触发递归类型错误,如下所示:

¥Due to TypeScript limitations, recursive type inference can be finicky, and it only works in certain scenarios. Some more complicated types may trigger recursive type errors like this:

const Activity = z.object({
  name: z.string(),
  get subactivities() {
    // ^ ❌ 'subactivities' implicitly has return type 'any' because it does not 
    // // have a return type annotation and is referenced directly or indirectly 
    // in one of its return expressions.ts(7023)
 
    return z.union([z.null(), Activity]);
  },
});

在这些情况下,你可以在有问题的 getter 上添加类型注释来解析错误:

¥In these cases, you can resolve the error with a type annotation on the offending getter:

const Activity = z.object({
  name: z.string(),
  get subactivities(): z.ZodUnion<[z.ZodNull, typeof Activity]> { // ✅
    return z.union([z.null(), Activity]); 
  },
});

数组

¥Arrays

定义一个数组模式:

¥To define an array schema:

const stringArray = z.array(z.string()); // or z.string().array()

访问数组元素的内部模式。

¥To access the inner schema for an element of the array.

stringArray.unwrap(); // => string schema

Zod 实现了一些特定于数组的验证:

¥Zod implements a number of array-specific validations:

z.array(z.string()).min(5); // must contain 5 or more items
z.array(z.string()).max(5); // must contain 5 or fewer items
z.array(z.string()).length(5); // must contain 5 items exactly

元组

¥Tuples

与数组不同,元组通常是固定长度的数组,每个索引指定不同的模式。

¥Unlike arrays, tuples are typically fixed-length arrays that specify different schemas for each index.

const MyTuple = z.tuple([
  z.string(),
  z.number(),
  z.boolean()
]);
 
type MyTuple = z.infer<typeof MyTuple>;
// [string, number, boolean]

要添加可变参数 ("rest"):

¥To add a variadic ("rest") argument:

const variadicTuple = z.tuple([z.string()], z.number());
// => [string, ...number[]];

联合

¥Unions

联合类型 (A | B) 表示逻辑上的 "OR"。Zod 联合模式将按顺序根据每个选项检查输入。返回第一个验证成功的值。

¥Union types (A | B) represent a logical "OR". Zod union schemas will check the input against each option in order. The first value that validates successfully is returned.

const stringOrNumber = z.union([z.string(), z.number()]);
// string | number
 
stringOrNumber.parse("foo"); // passes
stringOrNumber.parse(14); // passes

要提取内部选项模式:

¥To extract the internal option schemas:

stringOrNumber.options; // [ZodString, ZodNumber]

可区分联合

¥Discriminated unions

可区分联合 是一种特殊的联合,其中 a) 所有选项都是对象模式,并且 b) 共享一个特定的键("discriminator")。根据 discriminator 键的值,TypeScript 能够如你所愿对类型签名进行 "narrow" 验证。

¥A discriminated union is a special kind of union in which a) all the options are object schemas that b) share a particular key (the "discriminator"). Based on the value of the discriminator key, TypeScript is able to "narrow" the type signature as you'd expect.

type MyResult =
  | { status: "success"; data: string }
  | { status: "failed"; error: string };
 
function handleResult(result: MyResult){
  if(result.status === "success"){
    result.data; // string
  } else {
    result.error; // string
  }
}

你可以使用常规的 z.union() 来表示它。但常规的联合操作比较简单 - 它们按顺序根据每个选项检查输入,并返回第一个通过的选项。对于大型联合来说,这可能会很慢。

¥You could represent it with a regular z.union(). But regular unions are naive—they check the input against each option in order and return the first one that passes. This can be slow for large unions.

因此,Zod 提供了一个使用鉴别键的 z.discriminatedUnion() API,以提高解析效率。

¥So Zod provides a z.discriminatedUnion() API that uses a discriminator key to make parsing more efficient.

const MyResult = z.discriminatedUnion("status", [
  z.object({ status: z.literal("success"), data: z.string() }),
  z.object({ status: z.literal("failed"), error: z.string() }),
]);

交叉点

¥Intersections

交叉类型 (A & B) 表示逻辑上的 "AND"。

¥Intersection types (A & B) represent a logical "AND".

const a = z.union([z.number(), z.string()]);
const b = z.union([z.number(), z.boolean()]);
const c = z.intersection(a, b);
 
type c = z.infer<typeof c>; // => number

这对于交叉两种对象类型很有用。

¥This can be useful for intersecting two object types.

const Person = z.intersection({ name: z.string() });
type Person = z.infer<typeof Person>;
 
const Employee = z.intersection({ role: z.string() });
type Employee = z.infer<typeof Employee>;
 
const EmployedPerson = z.intersection(Person, Employee);
type EmployedPerson = z.infer<typeof EmployedPerson>;
// Person & Employee

在大多数情况下,最好使用 A.extend(B) 来合并两个对象模式。这种方法返回一个新的对象模式,而 z.intersection(A, B) 返回一个 ZodIntersection 实例,它缺少像 pickomit 这样的通用对象方法。

¥In most cases, it is better to use A.extend(B) to merge two object schemas. This approach returns a new object schema, whereas z.intersection(A, B) returns a ZodIntersection instance which lacks common object methods like pick and omit.

记录

¥Records

阅读 Record<string, number> 了解更多信息。

¥Record schemas are used to validate types such as Record<string, number>.

const IdCache = z.record(z.string(), z.string());
type IdCache = z.infer<typeof IdCache>; // Record<string, string>
 
IdCache.parse({
  carlotta: "77d2586b-9e8e-4ecf-8b21-ea7e0530eadd",
  jimmie: "77d2586b-9e8e-4ecf-8b21-ea7e0530eadd",
});

键模式可以是任何可分配给 string | number | symbol 的 Zod 模式。

¥The key schema can be any Zod schema that is assignable to string | number | symbol.

const Keys = z.union([z.string(), z.number(), z.symbol()]);
const AnyObject = z.record(Keys, z.unknown());
// Record<string | number | symbol, unknown>

要创建包含枚举定义的键的对象架构:

¥To create an object schemas containing keys defined by an enum:

const Keys = z.enum(["id", "name", "email"]);
const Person = z.record(Keys, z.string());
// { id: string; name: string; email: string }

Zod 4 — 在 Zod 4 中,如果你将 z.enum 作为 z.record() 的第一个参数传递,Zod 将全面检查输入中所有枚举值是否作为键存在。此行为与 TypeScript 一致:

¥Zod 4 — In Zod 4, if you pass a z.enum as the first argument to z.record(), Zod will exhaustively check that all enum values exist in the input as keys. This behavior agrees with TypeScript:

type MyRecord = Record<"a" | "b", string>;
const myRecord: MyRecord = { a: "foo", b: "bar" }; // ✅
const myRecord: MyRecord = { a: "foo" }; // ❌ missing required key `b`

在 Zod 3 中,未进行详尽性检查。要复制 Zod 3 的行为,请使用 z.partialRecord()

¥In Zod 3, exhaustiveness was not checked. To replicate the Zod 3 behavior, use z.partialRecord().

如果你需要部分记录类型,请使用 z.partialRecord()。这将跳过 Zod 通常使用 z.enum()z.literal() 密钥模式运行的特殊详尽性检查。

¥If you want a partial record type, use z.partialRecord(). This skips the special exhaustiveness checks Zod normally runs with z.enum() and z.literal() key schemas.

const Keys = z.enum(["id", "name", "email"]).or(z.never()); 
const Person = z.partialRecord(Keys, z.string());
// { id?: string; name?: string; email?: string }

映射

¥Maps

const StringNumberMap = z.map(z.string(), z.number());
type StringNumberMap = z.infer<typeof StringNumberMap>; // Map<string, number>
 
const myMap: StringNumberMap = new Map();
myMap.set("one", 1);
myMap.set("two", 2);
 
StringNumberMap.parse(myMap);

集合

¥Sets

const NumberSet = z.set(z.number());
type NumberSet = z.infer<typeof NumberSet>; // Set<number>
 
const mySet: NumberSet = new Set();
mySet.add(1);
mySet.add(2);
NumberSet.parse(mySet);

可以使用以下实用方法进一步约束设置的 Schema。

¥Set schemas can be further constrained with the following utility methods.

z.set(z.string()).min(5); // must contain 5 or more items
z.set(z.string()).max(5); // must contain 5 or fewer items
z.set(z.string()).size(5); // must contain 5 items exactly

Promise

已弃用 — z.promise() 在 Zod 4 中已弃用。Promise 模式的有效用例极其有限。如果你怀疑某个值可能是 Promise,只需在使用 Zod 解析之前对其进行 await 转换即可。

¥Deprecatedz.promise() is deprecated in Zod 4. There are vanishingly few valid uses cases for a Promise schema. If you suspect a value might be a Promise, simply await it before parsing it with Zod.

Instanceof

你可以使用 z.instanceof 检查输入是否是某个类的实例。这对于针对从第三方库导出的类验证输入非常有用。

¥You can use z.instanceof to check that the input is an instance of a class. This is useful to validate inputs against classes that are exported from third-party libraries.

class Test {
  name: string;
}
 
const TestSchema = z.instanceof(Test);
 
TestSchema.parse(new Test()); // ✅
TestSchema.parse("whatever"); // ❌

细化

¥Refinements

每个 Zod 模式都存储一个改进数组。细化是一种执行自定义验证的方法,Zod 并未提供原生 API。

¥Every Zod schema stores an array of refinements. Refinements are a way to perform custom validation that Zod doesn't provide a native API for.

.refine()

const myString = z.string().refine((val) => val.length <= 255);

细化函数不应该抛出异常。它们应该返回一个假值来表示失败。Zod 无法捕获抛出的错误。

¥Refinement functions should never throw. Instead they should return a falsy value to signal failure. Thrown errors are not caught by Zod.

要自定义错误消息:

¥To customize the error message:

const myString = z.string().refine((val) => val.length > 8, { 
  error: "Too short!" 
});

默认情况下,检查中的验证问题被认为是可继续的;也就是说,Zod 将按顺序执行所有检查,即使其中一个检查导致验证错误。这通常是可取的,因为这意味着 Zod 可以一次性显示尽可能多的错误。

¥By default, validation issues from checks are considered continuable; that is, Zod will execute all checks in sequence, even if one of them causes a validation error. This is usually desirable, as it means Zod can surface as many errors as possible in one go.

const myString = z.string()
  .refine((val) => val.length > 8)
  .refine((val) => val === val.toLowerCase());
  
 
const result = myString.safeParse("OH NO");
result.error.issues;
/* [
  { "code": "custom", "message": "Too short!" },
  { "code": "custom", "message": "Must be lowercase" }
] */

要将特定细化标记为不连续,请使用 abort 参数。如果检查失败,验证将终止。

¥To mark a particular refinement as non-continuable, use the abort parameter. Validation will terminate if the check fails.

const myString = z.string()
  .refine((val) => val.length > 8, { abort: true })
  .refine((val) => val === val.toLowerCase());
 
 
const result = myString.safeParse("OH NO");
result.error!.issues;
// => [{ "code": "custom", "message": "Too short!" }]

要自定义错误路径,请使用 path 参数。这通常仅在对象模式的上下文中有用。

¥To customize the error path, use the path parameter. This is typically only useful in the context of object schemas.

const passwordForm = z
  .object({
    password: z.string(),
    confirm: z.string(),
  })
  .refine((data) => data.password === data.confirm, {
    message: "Passwords don't match",
    path: ["confirm"], // path of error
  });

这将在相关问题中设置 path 参数:

¥This will set the path parameter in the associated issue:

const result = passwordForm.safeParse({ password: "asdf", confirm: "qwer" });
result.error.issues;
/* [{
  "code": "custom",
  "path": [ "confirm" ],
  "message": "Passwords don't match"
}] */

可以对 async 进行改进:

¥Refinements can be async:

const userId = z.string().refine(async (id) => {
  // verify that ID exists in database
  return true;
});

如果你使用异步细化,则必须使用 .parseAsync 方法来解析数据!否则 Zod 将抛出错误。

¥If you use async refinements, you must use the .parseAsync method to parse data! Otherwise Zod will throw an error.

const result = await userId.parseAsync("abc123");

.superRefine()

在 Zod 4 中,.superRefine() 已被弃用,取而代之的是 .check()

¥In Zod 4, .superRefine() has been deprecated in favor of .check()

.check()

.refine() API 是更通用(且冗长)的 API .check() 上的语法糖。你可以使用此 API 在单个细化中创建多个问题,或者完全控制生成的问题对象。

¥The .refine() API is syntactic sugar atop a more versatile (and verbose) API called .check(). You can use this API to create multiple issues in a single refinement or have full control of the generated issue objects.

const UniqueStringArray = z.array(z.string()).check((ctx) => {
  if (ctx.value.length > 3) {
    ctx.issues.push({
      code: "too_big",
      maximum: 3,
      origin: "array",
      inclusive: true,
      message: "Too many items 😡",
      input: ctx.value
    });
  }
 
  if (ctx.value.length !== new Set(ctx.value).size) {
    ctx.issues.push({
      code: "custom",
      message: `No duplicates allowed.`,
      input: ctx.value,
      continue: true // make this issue continuable (default: false)
    });
  }
});

常规 .refine API 仅会生成带有 "custom" 错误代码的问题,但 .check() 可以抛出其他类型的问题。有关 Zod 内部问题类型的更多信息,请阅读 错误自定义 文档。

¥The regular .refine API only generates issues with a "custom" error code, but .check() makes it possible to throw other issue types. For more information on Zod's internal issue types, read the Error customization docs.

管道

¥Pipes

Schema 可以链接在一起形成 "pipes"。管道主要在与 转换 结合使用时有用。

¥Schemas can be chained together into "pipes". Pipes are primarily useful when used in conjunction with Transforms.

const stringToLength = z.string().pipe(z.transform(val => val.length));
 
stringToLength.parse("hello"); // => 5

转换

¥Transforms

转换是一种特殊的模式。它们不再验证输入,而是接受任何数据并对数据执行某种转换。要定义转换:

¥Transforms are a special kind of schema. Instead of validating input, they accept anything and perform some transformation on the data. To define a transform:

const castToString = z.transform((val) => String(val));
 
castToString.parse("asdf"); // => "asdf"
castToString.parse(123); // => "123"
castToString.parse(true); // => "true"

要在转换内部执行验证逻辑,请使用 ctx。要报告验证问题,请将新问题推送到 ctx.issues(类似于 .check() API)。

¥To perform validation logic inside a transform, use ctx. To report a validation issue, push a new issue onto ctx.issues (similar to the .check() API).

const coercedInt = z.transform((val, ctx) => {
  try {
    const parsed = Number.parseInt(String(val));
    return parsed;
  } catch (e) {
    ctx.issues.push({
      code: "custom",
      message: "Not a number",
      input: val,
    });
 
    // this is a special constant with type `never`
    // returning it lets you exit the transform without impacting the inferred return type
    return z.NEVER; 
  }
});

通常,转换操作与 管道 结合使用。这种组合对于执行一些初始验证,然后将解析后的数据转换为另一种形式非常有用。

¥Most commonly, transforms are used in conjunction with Pipes. This combination is useful for performing some initial validation, then transforming the parsed data into another form.

const stringToLength = z.string().pipe(z.transform(val => val.length));
 
stringToLength.parse("hello"); // => 5

.transform()

将某些模式通过管道传输到转换中是一种常见的模式,因此 Zod 提供了一种便捷的 .transform() 方法。

¥Piping some schema into a transform is a common pattern, so Zod provides a convenience .transform() method.

const stringToLength = z.string().transform(val => val.length); 

转换也可以是异步的:

¥Transforms can also be async:

const idToUser = z
  .string()
  .transform(async (id) => {
    // fetch user from database
    return db.getUserById(id); 
  });
 
const user = await idToUser.parseAsync("abc123");

如果你使用异步转换,则解析数据时必须使用 .parseAsync.safeParseAsync!否则 Zod 将抛出错误。

¥If you use async transforms, you must use a .parseAsync or .safeParseAsync when parsing data! Otherwise Zod will throw an error.

.preprocess()

将转换通过管道传输到另一个模式是另一种常见模式,因此 Zod 提供了一个便捷的 z.preprocess() 函数。

¥Piping a transform into another schema is another common pattern, so Zod provides a convenience z.preprocess() function.

const coercedInt = z.preprocess((val) => {
  if (typeof val === "string") {
    return Number.parseInt(val);
  }
  return val;
}, z.int());

默认值

¥Defaults

要设置模式的默认值:

¥To set a default value for a schema:

const defaultTuna = z.string().default("tuna");
 
defaultTuna.parse(undefined); // => "tuna"

或者,你可以传递一个函数,每当需要生成默认值时,该函数都会重新执行:

¥Alternatively, you can pass a function which will be re-executed whenever a default value needs to be generated:

const randomDefault = z.number().default(Math.random);
 
randomDefault.parse(undefined);    // => 0.4413456736055323
randomDefault.parse(undefined);    // => 0.1871840107401901
randomDefault.parse(undefined);    // => 0.7223408162401552

预错误

¥Prefaults

在 Zod 中,设置默认值将缩短解析过程。如果输入是 undefined,则立即返回默认值。因此,默认值必须可以赋值给 Schema 的输出类型。

¥In Zod, setting a default value will short-circuit the parsing process. If the input is undefined, the default value is eagerly returned. As such, the default value must be assignable to the output type of the schema.

const schema = z.string().transform(val => val.length).default(0);
schema.parse(undefined); // => 0

有时,定义一个预故障 ("预解析默认值") 值很有用。如果输入是 undefined,则将解析预设值。解析过程没有短路。因此,默认值必须可以赋值给 Schema 的输入类型。

¥Sometimes, it's useful to define a prefault ("pre-parse default") value. If the input is undefined, the prefault value will be parsed instead. The parsing process is not short circuited. As such, the prefault value must be assignable to the input type of the schema.

z.string().transform(val => val.length).prefault("tuna");
schema.parse(undefined); // => 4

如果你想通过一些可变的改进传递一些输入值,这也很有用。

¥This is also useful if you want to pass some input value through some mutating refinements.

const a = z.string().trim().toUpperCase().prefault("  tuna  ");
a.parse(undefined); // => "TUNA"
 
const b = z.string().trim().toUpperCase().default("  tuna  ");
b.parse(undefined); // => "  tuna  "

捕获

¥Catch

使用 .catch() 定义在发生验证错误时返回的后备值:

¥Use .catch() to define a fallback value to be returned in the event of a validation error:

const numberWithCatch = z.number().catch(42);
 
numberWithCatch.parse(5); // => 5
numberWithCatch.parse("tuna"); // => 42

或者,你可以传递一个函数,每当需要生成捕获值时,该函数都会重新执行。

¥Alternatively, you can pass a function which will be re-executed whenever a catch value needs to be generated.

const numberWithRandomCatch = z.number().catch((ctx) => {
  ctx.error; // the caught ZodError
  return Math.random();
});
 
numberWithRandomCatch.parse("sup"); // => 0.4413456736055323
numberWithRandomCatch.parse("sup"); // => 0.1871840107401901
numberWithRandomCatch.parse("sup"); // => 0.7223408162401552

品牌类型

¥Branded types

TypeScript 的类型系统是 structural,这意味着两个结构等效的类型被视为相同。

¥TypeScript's type system is structural, meaning that two types that are structurally equivalent are considered the same.

type Cat = { name: string };
type Dog = { name: string };
 
const pluto: Dog = { name: "pluto" };
const simba: Cat = fido; // works fine

在某些情况下,可能需要在 TypeScript 中模拟 名义类型。这可以通过品牌类型(也称为 "不透明类型")实现。

¥In some cases, it can be desirable to simulate nominal typing inside TypeScript. This can be achieved with branded types (also known as "opaque types").

const Cat = z.object({ name: z.string() }).brand<"Cat">();
const Dog = z.object({ name: z.string() }).brand<"Dog">();
 
type Cat = z.infer<typeof Cat>; // { name: string } & z.$brand<"Cat">
type Dog = z.infer<typeof Dog>; // { name: string } & z.$brand<"Dog">
 
const pluto = Dog.parse({ name: "pluto" });
const simba: Cat = pluto; // ❌ not allowed

在底层,这是通过将 "brand" 附加到架构的推断类型来实现的。

¥Under the hood, this works by attaching a "brand" to the schema's inferred type.

const Cat = z.object({ name: z.string() }).brand<"Cat">();
type Cat = z.infer<typeof Cat>; // { name: string } & z.$brand<"Cat">

使用此品牌,任何普通(无品牌)数据结构都不再可分配给推断的类型。你必须使用模式解析一些数据才能获得品牌数据。

¥With this brand, any plain (unbranded) data structures are no longer assignable to the inferred type. You have to parse some data with the schema to get branded data.

请注意,品牌类型不会影响 .parse 的运行时结果。它是一个纯静态的构造函数。

¥Note that branded types do not affect the runtime result of .parse. It is a static-only construct.

只读

¥Readonly

要将模式标记为只读:

¥To mark a schema as readonly:

const ReadonlyUser = z.object({ name: z.string() }).readonly();
type ReadonlyUser = z.infer<typeof ReadonlyUser>;
// Readonly<{ name: string }>

这将返回一个封装原始模式的新模式。新模式的推断类型将标记为 readonly。请注意,这仅影响 TypeScript 中的对象、数组、元组、SetMap

¥This returns a new schema that wraps the original. The new schema's inferred type will be marked as readonly. Note that this only affects objects, arrays, tuples, Set, and Map in TypeScript:

z.object({ name: z.string() }).readonly(); // { readonly name: string }
z.array(z.string()).readonly(); // readonly string[]
z.tuple([z.string(), z.number()]).readonly(); // readonly [string, number]
z.map(z.string(), z.date()).readonly(); // ReadonlyMap<string, Date>
z.set(z.string()).readonly(); // ReadonlySet<string>

输入将使用原始模式进行解析,然后使用 Object.freeze() 冻结结果以防止修改。

¥Inputs will be parsed using the original schema, then the result will be frozen with Object.freeze() to prevent modifications.

const result = ReadonlyUser.parse({ name: "fido" });
result.name = "simba"; // throws TypeError

模板字面量

¥Template literals

Zod 4 中的新功能

¥New in Zod 4

定义一个模板字面量模式:

¥To define a template literal schema:

const schema = z.templateLiteral("hello, ", z.string(), "!");
// `hello, ${string}!`

z.templateLiteral API 可以处理任意数量的字符串字面量(例如 "hello")和模式。任何具有可赋值给 string | number | bigint | boolean | null | undefined 的推断类型的 Schema 都可以传递。

¥The z.templateLiteral API can handle any number of string literals (e.g. "hello") and schemas. Any schema with an inferred type that's assignable to string | number | bigint | boolean | null | undefined can be passed.

z.templateLiteral([ "hi there" ]); 
// `hi there`
 
z.templateLiteral([ "email: ", z.string()]); 
// `email: ${string}`
 
z.templateLiteral([ "high", z.literal(5) ]); 
// `high5`
 
z.templateLiteral([ z.nullable(z.literal("grassy")) ]); 
// `grassy` | `null`
 
z.templateLiteral([ z.number(), z.enum(["px", "em", "rem"]) ]); 
// `${number}px` | `${number}em` | `${number}rem`

JSON

要验证任何可 JSON 编码的值:

¥To validate any JSON-encodable value:

const jsonSchema = z.json();

这是一个便捷的 API,它返回以下联合模式:

¥This is a convenience API that returns the following union schema:

const jsonSchema = z.lazy(() => {
  return z.union([
    z.string(params), 
    z.number(), 
    z.boolean(), 
    z.null(), 
    z.array(jsonSchema), 
    z.record(z.string(), jsonSchema)
  ]);
});

自定义

¥Custom

你可以使用 z.custom() 为任何 TypeScript 类型创建 Zod 模式。这对于为 Zod 不支持的类型创建模式(例如模板字符串字面量)非常有用。

¥You can create a Zod schema for any TypeScript type by using z.custom(). This is useful for creating schemas for types that are not supported by Zod out of the box, such as template string literals.

const px = z.custom<`${number}px`>((val) => {
  return typeof val === "string" ? /^\d+px$/.test(val) : false;
});
 
type px = z.infer<typeof px>; // `${number}px`
 
px.parse("42px"); // "42px"
px.parse("42vw"); // throws;

如果你不提供验证函数,Zod 将允许任何值。这可能很危险!

¥If you don't provide a validation function, Zod will allow any value. This can be dangerous!

z.custom<{ arg: string }>(); // performs no validation

你可以通过传递第二个参数来自定义错误消息和其他选项。此参数的工作方式与 .refine 的 params 参数相同。

¥You can customize the error message and other options by passing a second argument. This parameter works the same way as the params parameter of .refine.

z.custom<...>((val) => ..., "custom error message");

函数

¥Functions

在 Zod 4 中,z.function() 不再返回 Zod 模式。

¥In Zod 4, z.function() no longer returns a Zod schema.

Zod 提供了一个 z.function() 实用程序,用于定义经过 Zod 验证的函数。这样,你可以避免将验证代码与业务逻辑混合在一起。

¥Zod provides a z.function() utility for defining Zod-validated functions. This way, you can avoid intermixing validation code with your business logic.

const MyFunction = z.function({
  input: [z.string()], // parameters (must be an array or a ZodTuple)
  output: z.number()  // return type
});

函数 Schema 有一个 .implement() 方法,它接受一个函数并返回一个新函数,该函数会自动验证其输入和输出。

¥Function schemas have an .implement() method which accepts a function and returns a new function that automatically validates its inputs and outputs.

const computeTrimmedLength = MyFunction.implement((input) => {
  // TypeScript knows x is a string!
  return input.trim().length;
});
 
trimmedLength("sandwich"); // => 8
trimmedLength(" asdf "); // => 4

如果输入无效,此函数将抛出 ZodError

¥This function will throw a ZodError if the input is invalid:

computeTrimmedLength(42); // throws ZodError

如果你只关心验证输入,则可以省略 output 字段。

¥If you only care about validating inputs, omit the output field is optional.

const MyFunction = z.function({
  input: [z.string()], // parameters (must be an array or a ZodTuple)
});
 
const computeTrimmedLength = MyFunction.implement((input) => input.trim.length);