← 中文资源

简体中文版本

Zod safeParse 错误格式化

用 Zod 的 safeParse 校验不可信载荷:聚合全部问题及字段路径,展平成以字段为键的错误消息映射供表单直接消费。

阅读英文原文

示例说明

外部输入在进入业务逻辑前必须经过类型与约束的双重关卡。parse 失败直接抛异常,适合快速失败;safeParse 返回判别联合,让调用方优雅处理。error.issues 携带每个问题的字段路径与消息,把其展平成字段到消息的映射后,前端表单可以逐字段渲染错误,无需解析嵌套结构。

技术要点

  • safeParse 返回判别联合不抛异常
  • issue.path 定位嵌套字段的精确位置
  • 字段级错误映射可直接对接表单状态

代码解读

validate 的返回类型显式区分成功值与错误表两个分支;循环里把路径拼接为键,首个消息优先保留避免覆盖。合法样例返回带默认值填充的强类型对象,非法样例中邮箱格式、年龄下限与空标签数组的问题被一次性收集。

使用建议

面向 API 的错误结构应统一约定字段键名规范。默认值只在数据完整时兜底,不要用默认掩盖脏数据。schema 复杂后可拆分为可组合的片段再 merge。

代码示例

import { z } from 'zod';

const PayloadSchema = z.object({
  email: z.string().email(),
  age: z.number().int().min(18).max(120),
  tags: z.array(z.string().min(1)).max(5),
  role: z.enum(['admin', 'editor', 'viewer']).default('viewer'),
});

type Payload = z.infer<typeof PayloadSchema>;

function validate(raw: unknown):
  { ok: true; value: Payload } | { ok: false; fields: Record<string, string> } {
  const result = PayloadSchema.safeParse(raw);
  if (result.success) return { ok: true, value: result.data };

  const fields: Record<string, string> = {};
  for (const issue of result.error.issues) {
    const key = issue.path.join('.') || '_root';
    fields[key] ??= issue.message;
  }
  return { ok: false, fields };
}

const good = validate({ email: '[email protected]', age: 30, tags: ['a'] });
const bad = validate({ email: 'nope', age: 15, tags: [] });

console.log(good.ok, bad.ok ? '' : JSON.stringify(bad.fields));