本文介绍在 Zod 中通过 .transform() 方法对整个解析后的对象进行转换,动态添加基于现有字段计算得出的新属性(如 statusLabel),避免重复定义字段或破坏类型安全。
本文介绍在 zod 中通过 `.transform()` 方法对整个解析后的对象进行转换,动态添加基于现有字段计算得出的新属性(如 statuslabel),避免重复定义字段或破坏类型安全。
在使用 Zod 进行 TypeScript 数据验证时,常需将原始 API 响应(如 status: 1)映射为更友好的前端语义(如 statusLabel: 'OK')。关键在于:不应在 z.object() 内部为派生字段单独声明 schema 并调用 .transform()——这会导致类型冲突(Zod 会尝试从输入中读取 statusLabel,而原始数据并不存在该字段),也违背了“派生即计算”的设计意图。
正确做法是:先定义基础对象 Schema,再对其整体结果调用 .transform(),在回调函数中解构原始值、注入新属性。示例如下:
import { z } from 'zod';export enum ReportMessageStatus { UNKNOWN = 0, OK = 1, UNREAD = 2, DELETED = 3,}function transformStatus(status: ReportMessageStatus): string { switch (status) { case ReportMessageStatus.OK: return 'OK'; case ReportMessageStatus.UNREAD: return 'Unread'; case ReportMessageStatus.DELETED: return 'Deleted'; default: return 'Unknown'; }}// ✅ 正确:先定义基础结构,再 transform 整个对象export const ReportMessageSchema = z .object({ id: z.string(), name: z.string(), status: z.nativeEnum(ReportMessageStatus), }) .transform((val) => ({ ...val, statusLabel: transformStatus(val.status), // 派生属性,不参与输入解析 }));// 使用示例const rawData = { id: '203923', name: 'best name', status: 1 };const parsed = ReportMessageSchema.parse(rawData);// 类型推导为:// { id: string; name: string; status: ReportMessageStatus; statusLabel: string; }console.log(parsed.statusLabel); // "OK"
⚠️ 注意事项:
通过这种模式,你既能保持 Zod 的强类型约束,又能灵活扩展业务所需的 UI 层语义字段。