示例说明
外部输入在进入业务逻辑前必须经过类型与约束的双重关卡。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));