如何使用Zod实现对象属性的灵活定义?
实现方案
你可以利用Zod的**区分联合(discriminated union)**特性,基于type字段区分动物和植物的Schema,同时保留公共属性在根层级、专属属性放在custom子对象里。具体实现步骤如下:
1. 抽离公共基础Schema
先把动物和植物共有的属性单独定义成基础Schema,避免重复代码:
import { z } from "zod"; import { t } from "../trpc"; // 替换成你的TRPC实例路径 // 公共属性Schema const BaseOrganismSchema = z.object({ id: z.string(), type: z.enum(["ANIMAL", "PLANT"]), // 用enum限定type可选值,比string更严谨 // 其他公共属性示例 name: z.string(), createdAt: z.date().optional(), });
2. 定义动物/植物的专属Schema
基于基础Schema扩展,分别指定type的固定值和custom字段的专属结构:
// 动物专属Schema const AnimalSchema = BaseOrganismSchema.extend({ type: z.literal("ANIMAL"), // 固定type为"ANIMAL",作为区分标识 custom: z.object({ fur: z.string(), // 动物专属属性:皮毛类型 legsCount: z.number().int().min(0), // 示例扩展属性 }), }); // 植物专属Schema const PlantSchema = BaseOrganismSchema.extend({ type: z.literal("PLANT"), // 固定type为"PLANT" custom: z.object({ seeds: z.string(), // 植物专属属性:种子类型 height: z.number().positive(), // 示例扩展属性 }), });
3. 创建区分联合Schema
用z.discriminatedUnion组合两个Schema,指定type作为区分字段:
const OrganismSchema = z.discriminatedUnion("type", [AnimalSchema, PlantSchema]);
4. 在TRPC Procedure中使用
将联合Schema作为input传入TRPC的procedure,同时TS会自动根据type推断custom的结构:
const create = t.procedure .input(OrganismSchema.nullish()) // 保留你原本的nullish可选性 .mutation(async ({ input }) => { // 根据type自动推断custom类型,获得完整TS提示 if (input?.type === "ANIMAL") { console.log(input.custom.fur); // 直接访问动物专属属性 } else if (input?.type === "PLANT") { console.log(input.custom.seeds); // 直接访问植物专属属性 } // 后续创建逻辑... return { success: true, data: input }; });
关键说明
z.discriminatedUnion第一个参数是区分字段名(这里是type),第二个参数是成员Schema数组;- 每个成员Schema必须用
z.literal()固定区分字段的具体值,这样Zod能准确校验类型,TS也能自动做类型推断; - 这种写法既复用了公共属性,又严格区分了不同类型的专属结构,同时能获得完整的类型提示。
内容的提问来源于stack exchange,提问作者antonwilhelm
相关产品推荐
相关产品推荐

