标题: Zod 4 是 TypeScript 优先的模式验证库的一次重写,已于 2025 年作为稳定的主版本发布。四处变化直接影响了我的代码:字符串格式迁移到顶层函数(使用
z.email()而非z.string().email())、四种错误选项合并为一个error参数、错误格式化迁移到独立辅助函数(z.flattenError、z.treeifyError、z.prettifyError),以及.strict()/.passthrough()变更为z.strictObject()/z.looseObject()。已弃用的 Zod 3 API 仍可继续使用(会显示警告),因此我逐步完成了迁移。
关键要点
- Zod 4 是 TypeScript 优先的模式验证器的稳定主版本,已于 2025 年发布;需要 TypeScript 5.5 或更高版本。
- 字符串格式现已升级为顶层可摇树优化的函数——
z.email()、z.uuid()、z.url()——而z.string().email()已被弃用但仍可使用。 - 单个
error参数取代了 Zod 3 中的message、invalid_type_error、required_error和errorMap。 - 错误格式化已迁移至
z.flattenError(表单字段)、z.treeifyError(嵌套结构)和z.prettifyError(人类可读字符串)。 zod/mini构建版本通过函数式、可摇树优化的 API 暴露相同的验证器;z.infer、.parse()和.safeParse()保持不变。
我在几乎每个项目中都会使用 Zod 来验证边界处的不可信输入——请求体、表单数据、环境变量、API 响应。Zod 4 对接口的改动足以让机械式升级影响到少量文件,因此我详细梳理了具体变化。
Zod 4 到底发生了哪些变化?
Zod 4 是 TypeScript 优先的模式验证库的一次彻底重写,已于 2025 年作为稳定的主版本发布。核心亮点是性能:Zod 团队在发布说明中指出,TypeScript 编译器实例化次数大幅减少,运行时解析速度更快,这对大型代码库尤为重要,因为模式类型往往主导类型检查耗时。四处 API 变化直接影响了我的代码——字符串格式迁移到顶层函数、错误选项合并为一个参数、错误格式化迁移到独立辅助函数,以及 .strict()/.passthrough() 变更为 z.strictObject()/z.looseObject()。
为什么 z.string().email() 变成了 z.email()?
Zod 4 将字符串格式提升为独立的顶层函数——z.email()、z.uuid()、z.url() 以及 z.iso 下的 ISO 辅助函数——而非挂在 z.string() 上的链式方法。链式形式仍可使用,但已被弃用并会发出警告。原因是摇树优化:每种格式都是独立函数,因此仅验证邮箱的打包不会再包含其他格式的逻辑。
import * as z from "zod";
const User = z.object({
id: z.uuid(),
email: z.email(),
website: z.url().optional(),
age: z.number().int().min(18),
});
type User = z.infer<typeof User>;
Enter fullscreen mode Exit fullscreen mode
如何在 Zod 4 中设置自定义错误消息?
Zod 4 用单个 error 参数取代了四种独立的错误选项。在 Zod 3 中需要传入 message、invalid_type_error、required_error 或完整的 errorMap。在 Zod 4 中只需传入一个 error:可以是固定消息的字符串,也可以是接收 issue 并返回消息的函数,从而在一个地方区分缺失值与类型错误。
// Zod 4: one error param — string or function
z.string({ error: "Name is required" });
z.string({
error: (issue) =>
issue.input === undefined ? "Name is required" : "Name must be text",
});
Enter fullscreen mode Exit fullscreen mode
如何将 ZodError 转换为表单错误?
Zod 4 将错误格式化迁移到三个顶层辅助函数。z.flattenError(error) 返回 { formErrors, fieldErrors },可直接映射到表单的字段级消息。z.treeifyError(error) 返回与模式形状一致的嵌套对象。z.prettifyError(error) 返回用于日志的人类可读多行字符串。原始的 error.issues 数组保持不变;error.format() 和 error.flatten() 已被弃用,请改用这些辅助函数。
const result = User.safeParse(await request.json());
if (!result.success) {
const { fieldErrors } = z.flattenError(result.error);
return Response.json({ errors: fieldErrors }, { status: 400 });
}
const user = result.data; // fully typed as User
Enter fullscreen mode Exit fullscreen mode
何时应使用 zod/mini 而非完整 zod 包?
当 bundle 大小至关重要时(客户端代码、边缘函数、已发布的组件),请使用 zod/mini;其他场景则使用完整 zod 包。zod/mini 通过函数式 API 暴露相同的验证器:不再使用链式 .optional(),而是使用 z.optional() 包裹;refinement 则使用 .check() 而非 .refine()。虽然更冗长,但由于没有方法链,未使用的代码可被摇树优化。两个构建版本共享同一核心,因此运行时行为完全一致。
import * as z from "zod/mini";
const User = z.object({
email: z.email(),
name: z.optional(z.string()),
});
Enter fullscreen mode Exit fullscreen mode
| 关注点 | zod(完整版) | zod/mini |
|---|---|---|
| API 风格 | 链式方法(.optional()) |
函数式包装(z.optional()) |
| Bundle 大小 | 较大 | 更小,支持摇树优化 |
| 易用性 | 流畅、可读性强 | 更冗长 |
| 验证器相同 | 是 | 是(相同核心) |
| 最佳适用场景 | 服务器、通用代码 | 边缘/客户端、尺寸敏感的 bundle |
如何从 Zod 3 迁移而不破坏现有代码?
请逐步迁移,因为 Zod 4 保留了已弃用的 Zod 3 API(仅显示警告而非直接移除)。在过渡期间,Zod 以 zod/v4 导入路径(在 [email protected] 中)发布新版本,允许你逐文件采用后再升级到 [email protected];旧版 API 仍可通过 zod/v3 访问。我的迁移流程为:升级包、运行类型检查和测试,然后分批清除弃用警告——重命名字符串格式调用、将错误选项合并为 error,以及将 .strict()/.passthrough() 切换到新的对象函数。z.infer、.parse() 和 .safeParse() 未发生变化,因此大多数模式无需改动即可继续工作。
常见问题
问:z.string().email() 在 Zod 4 中已移除吗?
答: 没有。它仍可使用,但已被弃用并会发出警告。推荐形式是顶层 z.email(),两者验证结果完全一致。
问:Zod 4 中什么取代了 errorMap 和 invalid_type_error?
答: 单个 error 参数——可以是固定消息的字符串,也可以是接收 issue 并返回条件消息的函数。
问:如何在 Zod 4 中获取表单的字段级错误?
答: 调用 z.flattenError(error);其 fieldErrors 会将每个模式键映射到对应的消息。使用 z.treeifyError 可处理嵌套结构。
问:zod/mini 是一个独立的库吗?
答: 不是。它是 Zod 4 的一个构建版本,通过函数式、可摇树优化的 API 暴露相同的验证器,以实现更小的 bundle。
问:Zod 4 是否需要特定版本的 TypeScript?
答: 是的。Zod 4 需要 TypeScript 5.5 或更高版本。
Originally published on devya.dev. Also on eng-ahmed.com. Built by Devya Solutions.
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.