Ahmed Mahmoud

标题: Zod 4 是 TypeScript 优先的模式验证库的一次重写,已于 2025 年作为稳定的主版本发布。四处变化直接影响了我的代码:字符串格式迁移到顶层函数(使用 z.email() 而非 z.string().email())、四种错误选项合并为一个 error 参数、错误格式化迁移到独立辅助函数(z.flattenErrorz.treeifyErrorz.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 中的 messageinvalid_type_errorrequired_errorerrorMap
  • 错误格式化已迁移至 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 中需要传入 messageinvalid_type_errorrequired_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.