关于 zod
Zod 是面向 TypeScript 的模式验证库,供需要运行时数据检查和类型推导的开发者使用。其 API 涵盖解析、验证、转换和 JSON Schema 转换,并提供支持摇树优化的 Mini 版本。
主要功能
- 01运行时解析与验证
- 02从模式推导类型
- 03数据转换与双向编解码
- 04导入和导出 JSON Schema
- 05支持摇树优化的 Zod Mini
适用场景
- 01运行时校验输入数据
- 02根据模式推导 TypeScript 类型
- 03与 JSON Schema 相互转换
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ marcioaltoe/ skills --skill zod安装后在 Claude Code 中输入/zod确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamlreferences/advanced.mdcodecs.mdjson-schema.mdmigration.mdzod-mini.md详细介绍
Zod 4
TypeScript 优先的模式验证。核心包体积为 2kb(gzip 压缩后)。零依赖。
重要:Zod 4 是当前稳定版本(zod@^4.0.0)。始终编写 Zod 4 代码。切勿使用已弃用的 Zod 3 模式。
导入
import * as z from "zod";
适用于支持 tree-shaking 的版本(包体积更小):
import * as z from "zod/mini";
核心模式
解析
schema.parse(data); // throws ZodError on failure
schema.safeParse(data); // returns { success, data?, error? }
await schema.parseAsync(data);
await schema.safeParseAsync(data);
类型推断
type MyType = z.infer<typeof mySchema>; // output type
type MyInput = z.input<typeof mySchema>; // input type
type MyOutput = z.output<typeof mySchema>; // same as z.infer
基本类型
z.string();
z.number(); // finite numbers only (no Infinity, no NaN)
z.bigint();
z.boolean();
z.symbol();
z.undefined();
z.null();
z.date();
z.nan();
强制转换
z.coerce.string(); // String(input)
z.coerce.number(); // Number(input)
z.coerce.boolean(); // Boolean(input)
z.coerce.bigint(); // BigInt(input)
z.coerce.date(); // new Date(input)
字符串格式(顶层)
使用顶层函数,而不是方法。像 z.string().email() 这样的方法已弃用。
// CORRECT (Zod 4)
z.email();
z.uuid();
z.url();
z.httpUrl();
z.hostname();
z.emoji();
z.base64();
z.base64url();
z.hex();
z.jwt();
z.nanoid();
z.cuid();
z.cuid2();
z.ulid();
z.ipv4();
z.ipv6();
z.mac();
z.cidrv4();
z.cidrv6();
z.hash("sha256"); // "sha1" | "sha384" | "sha512" | "md5"
z.e164(); // phone numbers
z.iso.date();
z.iso.time();
z.iso.datetime();
z.iso.duration();
// DEPRECATED (Zod 3 style — do NOT use)
z.string().email(); // ❌
z.string().uuid(); // ❌
z.string().url(); // ❌
UUID 版本
z.uuid(); // any RFC 9562/4122 UUID
z.uuid({ version: "v4" }); // specific version
z.uuidv4(); // convenience
z.uuidv6();
z.uuidv7();
z.guid(); // any 8-4-4-4-12 hex pattern (less strict)
自定义电子邮件正则表达式
z.email(); // default (Gmail rules)
z.email({ pattern: z.regexes.html5Email }); // browser-style
z.email({ pattern: z.regexes.rfc5322Email }); // RFC 5322
z.email({ pattern: z.regexes.unicodeEmail }); // intl emails
JWT
z.jwt();
z.jwt({ alg: "HS256" });
数字与整数
z.number(); // any finite number
z.int(); // safe integer range
z.int32(); // int32 range
z.float32(); // float32 range
z.float64(); // float64 range
// bigint variants
z.bigint();
z.int64();
z.uint64();
数字验证
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)
字符串验证
z.string().max(5);
z.string().min(5);
z.string().length(5);
z.string().regex(/pattern/);
z.string().startsWith("abc");
z.string().endsWith("xyz");
z.string().includes("---");
z.string().uppercase();
z.string().lowercase();
z.string().trim();
z.string().toLowerCase();
z.string().toUpperCase();
z.string().normalize();
对象
z.object({ name: z.string(), age: z.number() }); // strips unknown keys
z.strictObject({ name: z.string() }); // errors on unknown keys
z.looseObject({ name: z.string() }); // passes unknown keys through
已弃用:.strict()、.passthrough()、.strip()、.merge()、.deepPartial()。
对象方法
schema.extend({ newField: z.string() }); // add fields
schema.pick({ name: true }); // pick fields
schema.omit({ age: true }); // omit fields
schema.partial(); // all optional
schema.partial({ name: true }); // some optional
schema.required(); // all required
schema.keyof(); // ZodEnum from keys
schema.shape.name; // access inner schema
为获得最佳的 tsc 性能,建议使用展开语法:
z.object({ ...Base.shape, ...Extra.shape, newField: z.string() });
递归对象
const Category = z.object({
name: z.string(),
get subcategories() {
return z.array(Category);
},
});
枚举
z.enum(["A", "B", "C"]); // string enum
z.enum(MyTSEnum); // TypeScript enum (replaces z.nativeEnum())
z.enum({ A: 0, B: 1 } as const); // enum-like object
已弃用:z.nativeEnum()。请改用 z.enum()。
数组、元组、集合、映射
z.array(z.string());
z.array(z.string()).min(1).max(10).length(5);
z.tuple([z.string(), z.number()]);
z.tuple([z.string()], z.number()); // variadic: [string, ...number[]]
z.set(z.number());
z.map(z.string(), z.number());
联合与交集
z.union([z.string(), z.number()]);
z.discriminatedUnion("status", [
z.object({ status: z.literal("ok"), data: z.string() }),
z.object({ status: z.literal("err"), error: z.string() }),
]);
z.xor([z.string(), z.number()]); // exclusive union (exactly one match)
z.intersection(schemaA, schemaB);
记录
z.record(z.string(), z.number()); // Record<string, number>
z.record(z.enum(["a", "b"]), z.string()); // exhaustive: { a: string; b: string }
z.partialRecord(z.enum(["a", "b"]), z.string()); // partial: { a?: string; b?: string }
z.looseRecord(z.string().regex(/^x_/), z.number()); // pass through non-matching keys
重大变更:已移除单参数形式 z.record(z.string())。始终同时传入键和值的模式。
字面量
z.literal("hello");
z.literal(42);
z.literal(true);
z.literal(["a", "b", "c"]); // multi-value literal (new in Zod 4)
文件
z.file();
z.file().min(10_000); // min size in bytes
z.file().max(1_000_000); // max size in bytes
z.file().mime("image/png"); // single MIME
z.file().mime(["image/png", "image/jpeg"]); // multiple MIMEs
字符串布尔值
z.stringbool(); // "true"/"yes"/"1"/"on"/"y"/"enabled" → true, inverses → false
z.stringbool({ truthy: ["yes", "y"], falsy: ["no", "n"] });
z.stringbool({ case: "sensitive" });
模板字面量
z.templateLiteral(["hello, ", z.string()]); // `hello, ${string}`
z.templateLiteral([z.number(), z.enum(["px", "em", "rem"])]); // `${number}px` | ...
可选、可空、默认值
z.optional(z.string()); // or z.string().optional()
z.nullable(z.string()); // or z.string().nullable()
z.nullish(z.string()); // optional + nullable
z.string().default("hello"); // short-circuits on undefined, returns output type
z.string().prefault("hello"); // pre-parse default, runs through validation
z.number().catch(42); // fallback on any validation error
重大变更:.default() 现在接受输出类型,而非输入类型。要使用旧行为,请用 .prefault()。
转换与管道
z.transform((val) => String(val)); // standalone transform
z.string().transform((val) => val.length); // pipe string → transform
z.preprocess((val) => String(val), z.string()); // transform → pipe to schema
z.string().pipe(z.transform((val) => val.length)); // explicit pipe
// .overwrite() — transform without changing inferred type
z.number().overwrite((val) => val ** 2);
编解码器(双向转换)
Zod 4.1 新增。定义编码/解码对:
const stringToDate = z.codec(z.iso.datetime(), z.date(), {
decode: (s) => new Date(s),
encode: (d) => d.toISOString(),
});
stringToDate.decode("2024-01-15T10:30:00.000Z"); // → Date
stringToDate.encode(new Date()); // → string
stringToDate.parse("2024-01-15T10:30:00.000Z"); // → Date (same as decode at runtime)
错误自定义
使用统一的 error 参数(取代 message、errorMap、invalid_type_error、required_error):
z.string().min(5, { error: "Too short" });
z.string({ error: (issue) => issue.input === undefined ? "Required" : "Not a string" });
z.string({ error: (issue) => {
if (issue.code === "too_small") return `Min ${issue.minimum}`;
}});
已弃用:message、errorMap、invalid_type_error、required_error。
元数据与注册表
z.string().meta({ id: "email", title: "Email", description: "User email" });
z.string().describe("A description"); // shorthand for .meta({ description: ... })
// Custom registries
const myReg = z.registry<{ description: string }>();
z.string().register(myReg, { description: "..." });
JSON Schema
z.toJSONSchema(schema); // Zod → JSON Schema
z.toJSONSchema(schema, { target: "draft-07" }); // specific draft
z.toJSONSchema(schema, { target: "openapi-3.0" }); // OpenAPI 3.0
z.fromJSONSchema(jsonSchema); // JSON Schema → Zod (experimental)
细化
z.string().refine((val) => val.includes("@"), { error: "Must contain @" });
z.string().refine((val) => val.includes("@"), { error: "...", abort: true }); // stop on failure
z.array(z.string()).superRefine((val, ctx) => {
ctx.addIssue({ code: "custom", message: "...", input: val });
});
细化现在位于模式内部(不再由 ZodEffects 包装)。可以将 .refine() 与其他方法交错使用:
z.string().refine(v => v.includes("@")).min(5); // works in Zod 4!
错误美化输出
z.prettifyError(zodError); // formatted multi-line string
z.treeifyError(zodError); // tree structure (replaces .format() and .flatten())
更多参考
- Zod Mini:参见 references/zod-mini.md,了解支持 tree-shaking 的 API 差异、
.check()用法和包体积权衡 - 编解码器:参见 references/codecs.md,了解双向转换、编码行为和常见编解码器模式(stringToDate、jsonCodec 等)
- JSON Schema:参见 references/json-schema.md,了解
z.toJSONSchema()选项、格式转换、基于注册表的多模式以及z.fromJSONSchema() - 高级模式:参见 references/advanced.md,了解注册表、细化
when、.superRefine()、.check()、函数、品牌类型、只读、自定义模式以及高级字符串/对象/记录/联合选项 - 从 Zod 3 迁移:参见 references/migration.md,了解所有重大变更和已弃用的 API