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

如何将Zod可辨识联合转换为Zod枚举类型

从Zod联合类型提取kind字段生成枚举

现有代码

const DE = z.object({ kind: z.literal("DE"), value: z.literal("EUR") })
const AT = z.object({ kind: z.literal("AT"), value: z.literal("EUR") })
const CH = z.object({ kind: z.literal("CH"), value: z.literal("CHF") })

const countries = z.union([DE, AT, CH])

需求

实现一个extractKindsToEnum函数,提取上述联合类型中每个对象的kind字段值,生成对应的Zod枚举,同时保证类型推断正常工作:

const countryEnums = extractKindsToEnum(countries)

// 期望得到
// const countryEnums = z.enum(["DE", "AT", "CH"])

// 类型推断需满足
type TCountryEnum = z.infer<typeof countryEnums>
// 推断结果应为
// type TCountryEnum = "DE" | "AT" | "CH"

实现方案

1. 工具类型:提取kind字段的字面量类型

先定义类型工具,从联合类型的推断结果中提取kind的字面量集合:

type ExtractKind<T> = T extends { kind: infer K } ? K : never;

2. 运行时函数实现

利用Zod联合类型的options属性获取所有子schema,遍历提取kind的字面量值,再生成枚举:

import { z, ZodUnion, ZodObject, ZodLiteral } from "zod";

function extractKindsToEnum<
  T extends ZodUnion<[ZodObject<{ kind: ZodLiteral<string> }, any, any>, ...ZodObject<{ kind: ZodLiteral<string> }, any, any>[]]>
>(unionSchema: T) {
  const kindValues = unionSchema.options.map(schema => {
    return schema.shape.kind.value;
  }) as ExtractKind<z.infer<T>>[];

  return z.enum(kindValues);
}

3. 使用验证

代入示例代码测试,完全符合需求:

const DE = z.object({ kind: z.literal("DE"), value: z.literal("EUR") })
const AT = z.object({ kind: z.literal("AT"), value: z.literal("EUR") })
const CH = z.object({ kind: z.literal("CH"), value: z.literal("CHF") })

const countries = z.union([DE, AT, CH])

const countryEnums = extractKindsToEnum(countries);
// 等价于 z.enum(["DE", "AT", "CH"])

type TCountryEnum = z.infer<typeof countryEnums>;
// 类型为 "DE" | "AT" | "CH"

说明

  • 泛型约束确保传入的必须是包含带kind字面量字段对象schema的联合类型,避免非法输入。
  • 通过Zod暴露的options和shape属性稳定获取运行时的schema信息,无需额外类型断言。
  • 类型工具ExtractKind与运行时逻辑对齐,保证类型推断的准确性。

内容的提问来源于stack exchange,提问作者robkuz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 05:22:46