zod4 by kastalien-research/thoughtbox-dot-claude
npx skills add https://github.com/kastalien-research/thoughtbox-dot-claude --skill zod4Zod 4 是一个重大版本发布,带来了显著的性能改进、更短的 TypeScript 编译时间以及更简洁的 API。本技能涵盖了从 v3 迁移以及符合 Zod 4 习惯的用法。
在深入探讨之前,请先处理这些影响重大的破坏性变更:
| 变更 | Zod 3 | Zod 4 |
|---|---|---|
| Record 模式 | z.record(z.string()) | z.record(z.string(), z.string()) |
| 严格对象 | .strict() | z.strictObject({...}) |
| 透传 | .passthrough() |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
z.looseObject({...}) |
| 错误格式化 | err.format() | z.treeifyError(err) |
| 强制输入类型 | string | unknown |
安装 Zod 4:
npm install zod@^4.0.0
关于详细的破坏性变更,请参阅 ./reference/breaking-changes.md。
// Zod 3 (在 v4 中已失效)
z.record(z.string());
// Zod 4 (必需)
z.record(z.string(), z.string());
// Zod 3
z.object({ name: z.string() }).strict();
z.object({ name: z.string() }).passthrough();
// Zod 4
z.strictObject({ name: z.string() });
z.looseObject({ name: z.string() });
在 Zod 4 中,如果输入是 undefined,.default() 会短路并直接返回默认值(不经过解析)。如需旧行为,请使用 .prefault():
// Zod 4:默认值必须匹配 OUTPUT 类型
const schema = z.string()
.transform(val => val.length)
.default(0); // 直接返回 0,不经过解析
// 要解析默认值(旧行为):
const schema = z.string()
.transform(val => val.length)
.prefault("tuna"); // "tuna" 被解析 → 4
// Zod 3
const formatted = err.format();
const flat = err.flatten();
// Zod 4
const tree = z.treeifyError(err);
// 添加问题
err.issues.push({ /* 新问题 */ });
const schema = z.coerce.string();
type Input = z.input<typeof schema>;
// Zod 3: string
// Zod 4: unknown
const fileSchema = z.file()
.min(10_000) // 最小字节数
.max(1_000_000) // 最大字节数
.mime(["image/png", "image/jpeg"]);
const css = z.templateLiteral([z.number(), z.enum(["px", "em", "rem"])]);
// `${number}px` | `${number}em` | `${number}rem`
const email = z.templateLiteral([
z.string().min(1),
"@",
z.string().max(64),
]);
z.string().meta({
id: "email_address",
title: "电子邮件地址",
description: "用户的电子邮件",
examples: ["user@example.com"]
});
z.globalRegistry.add(mySchema, {
id: "user_schema",
title: "用户",
description: "用户数据结构"
});
import { z } from "zod";
import { en } from "zod/locales/en";
z.config(z.locales.en()); // 配置错误消息
// 拒绝未知键
z.strictObject({ name: z.string() });
// 允许未知键(透传)
z.looseObject({ name: z.string() });
完整的新功能指南,请参阅 ./reference/new-features.md。
Zod Mini (zod/mini) 提供了一个更小的捆绑包,具有可树摇的功能性 API:
import * as z from "zod/mini";
// 功能性检查而非方法
const schema = z.pipe(
z.string(),
z.minLength(1),
z.maxLength(100),
z.regex(/^[a-z]+$/)
);
// 可用函数
z.lt(value);
z.gt(value);
z.positive();
z.negative();
z.minLength(value);
z.maxLength(value);
z.regex(pattern);
z.trim();
z.toLowerCase();
z.toUpperCase();
搜索并替换:
// 查找
z.record(valueSchema)
// 替换为
z.record(z.string(), valueSchema)
// 查找
z.object({...}).strict()
// 替换为
z.strictObject({...})
// 查找
try {
schema.parse(data);
} catch (err) {
if (err instanceof z.ZodError) {
const formatted = err.format();
}
}
// 替换为
try {
schema.parse(data);
} catch (err) {
if (err instanceof z.ZodError) {
const tree = z.treeifyError(err);
}
}
如果对转换使用 .default(),请检查默认值是否匹配输出类型:
// 如果这个出错:
z.string().transform(s => s.length).default("hello")
// 改为:
z.string().transform(s => s.length).prefault("hello")
// 或者
z.string().transform(s => s.length).default(5) // 匹配输出类型
完整的迁移清单,请参阅 ./reference/migration-checklist.md。
| 错误 | 原因 | 修复 |
|---|---|---|
Expected 2 arguments, got 1 | z.record() 单参数 | 添加键模式:z.record(z.string(), ...) |
Property 'strict' does not exist | .strict() 已移除 | 使用 z.strictObject() |
Property 'format' does not exist | .format() 已移除 | 使用 z.treeifyError(err) |
.default() 类型不匹配 | 默认值必须匹配输出 | 使用 .prefault() 或修复默认值类型 |
有一个社区维护的代码修改工具可用:
npx zod-v3-to-v4
这可以自动化许多破坏性变更的修复。
每周安装次数
79
仓库
GitHub 星标数
6
首次出现
2026年1月20日
安全审计
安装于
opencode61
gemini-cli58
codex57
cursor56
github-copilot53
claude-code43
Zod 4 is a major release with significant performance improvements, reduced TypeScript compilation times, and a cleaner API. This skill covers migration from v3 and idiomatic Zod 4 usage.
Before diving deep, address these high-impact breaking changes:
| Change | Zod 3 | Zod 4 |
|---|---|---|
| Record schemas | z.record(z.string()) | z.record(z.string(), z.string()) |
| Strict objects | .strict() | z.strictObject({...}) |
| Passthrough | .passthrough() | z.looseObject({...}) |
| Error formatting | err.format() | z.treeifyError(err) |
| Coerce input type | string | unknown |
Install Zod 4:
npm install zod@^4.0.0
For detailed breaking changes, see ./reference/breaking-changes.md.
// Zod 3 (BROKEN in v4)
z.record(z.string());
// Zod 4 (REQUIRED)
z.record(z.string(), z.string());
// Zod 3
z.object({ name: z.string() }).strict();
z.object({ name: z.string() }).passthrough();
// Zod 4
z.strictObject({ name: z.string() });
z.looseObject({ name: z.string() });
In Zod 4, .default() short-circuits if input is undefined and returns the default directly (without parsing). Use .prefault() for the old behavior:
// Zod 4: default must match OUTPUT type
const schema = z.string()
.transform(val => val.length)
.default(0); // Returns 0 directly, not parsed
// To parse the default (old behavior):
const schema = z.string()
.transform(val => val.length)
.prefault("tuna"); // "tuna" is parsed → 4
// Zod 3
const formatted = err.format();
const flat = err.flatten();
// Zod 4
const tree = z.treeifyError(err);
// Adding issues
err.issues.push({ /* new issue */ });
const schema = z.coerce.string();
type Input = z.input<typeof schema>;
// Zod 3: string
// Zod 4: unknown
const fileSchema = z.file()
.min(10_000) // minimum bytes
.max(1_000_000) // maximum bytes
.mime(["image/png", "image/jpeg"]);
const css = z.templateLiteral([z.number(), z.enum(["px", "em", "rem"])]);
// `${number}px` | `${number}em` | `${number}rem`
const email = z.templateLiteral([
z.string().min(1),
"@",
z.string().max(64),
]);
z.string().meta({
id: "email_address",
title: "Email address",
description: "User's email",
examples: ["user@example.com"]
});
z.globalRegistry.add(mySchema, {
id: "user_schema",
title: "User",
description: "User data structure"
});
import { z } from "zod";
import { en } from "zod/locales/en";
z.config(z.locales.en()); // Configure error messages
// Rejects unknown keys
z.strictObject({ name: z.string() });
// Allows unknown keys (passthrough)
z.looseObject({ name: z.string() });
For complete new features guide, see ./reference/new-features.md.
Zod Mini (zod/mini) provides a smaller bundle with tree-shakable, functional API:
import * as z from "zod/mini";
// Functional checks instead of methods
const schema = z.pipe(
z.string(),
z.minLength(1),
z.maxLength(100),
z.regex(/^[a-z]+$/)
);
// Available functions
z.lt(value);
z.gt(value);
z.positive();
z.negative();
z.minLength(value);
z.maxLength(value);
z.regex(pattern);
z.trim();
z.toLowerCase();
z.toUpperCase();
Search and replace:
// Find
z.record(valueSchema)
// Replace with
z.record(z.string(), valueSchema)
// Find
z.object({...}).strict()
// Replace with
z.strictObject({...})
// Find
try {
schema.parse(data);
} catch (err) {
if (err instanceof z.ZodError) {
const formatted = err.format();
}
}
// Replace with
try {
schema.parse(data);
} catch (err) {
if (err instanceof z.ZodError) {
const tree = z.treeifyError(err);
}
}
If using .default() with transforms, check if default matches output type:
// If this breaks:
z.string().transform(s => s.length).default("hello")
// Change to:
z.string().transform(s => s.length).prefault("hello")
// OR
z.string().transform(s => s.length).default(5) // Match output type
For complete migration checklist, see ./reference/migration-checklist.md.
| Error | Cause | Fix |
|---|---|---|
Expected 2 arguments, got 1 | z.record() single arg | Add key schema: z.record(z.string(), ...) |
Property 'strict' does not exist | .strict() removed | Use z.strictObject() |
Property 'format' does not exist |
A community-maintained codemod is available:
npx zod-v3-to-v4
This automates many of the breaking change fixes.
Weekly Installs
79
Repository
GitHub Stars
6
First Seen
Jan 20, 2026
Security Audits
Gen Agent Trust HubFailSocketPassSnykPass
Installed on
opencode61
gemini-cli58
codex57
cursor56
github-copilot53
claude-code43
测试策略完整指南:单元/集成/E2E测试金字塔与自动化实践
11,200 周安装
.format() removed |
Use z.treeifyError(err) |
Type mismatch on .default() | Default must match output | Use .prefault() or fix default type |