You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 11:25:37