如何将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
相关产品推荐
相关产品推荐

