依赖ADT建模数据,如何简化TypeScript重复泛型约束的写法?
简化ADT泛型约束的几种方法
好问题!反复写那段冗长的泛型约束确实挺烦人的,尤其是在大量使用代数数据类型(ADT)建模的时候。这里有几个实用的简化方案:
1. 提取约束到类型别名
最直接的方式是把重复的keyof DiscriminatedMap约束抽成一个独立的类型别名,之后直接复用它:
type DiscriminatedMap = { kindA: { someRecordType: string } kindB: { someOtherRecordType: number } } // 提取重复的约束为类型别名 type DiscriminatedKey = keyof DiscriminatedMap; // 复用别名简化泛型声明 type DiscriminatedUnion<Key extends DiscriminatedKey = DiscriminatedKey> = DiscriminatedMap[Key] & { kind: Key }
这样不仅缩短了泛型约束的长度,还提升了代码的可读性——别人一看DiscriminatedKey就知道它指代的是DiscriminatedMap的键类型。
2. 拆分默认版本与泛型版本
如果你的DiscriminatedUnion绝大多数场景下都是使用默认的全联合类型(即keyof DiscriminatedMap),可以拆分出两个类型:一个是固定的全联合版本,另一个是保留灵活性的泛型版本:
type DiscriminatedMap = { kindA: { someRecordType: string } kindB: { someOtherRecordType: number } } // 灵活的泛型版本,仅在需要指定特定key时使用 type DiscriminatedUnion<Key extends keyof DiscriminatedMap> = DiscriminatedMap[Key] & { kind: Key } // 默认的全联合版本,日常直接用这个 type FullDiscriminatedUnion = DiscriminatedUnion<keyof DiscriminatedMap>
这种方式的好处是,日常使用时不需要写泛型参数,直接用FullDiscriminatedUnion即可;只有当你需要针对单个kind生成类型时,才会用到带泛型的DiscriminatedUnion<"kindA">。
3. 用映射类型自动生成全联合(进阶)
如果你的核心需求是生成完整的鉴别联合类型,甚至可以省略泛型参数,直接通过映射类型一次性生成:
type DiscriminatedMap = { kindA: { someRecordType: string } kindB: { someOtherRecordType: number } } // 直接生成完整的鉴别联合类型,无需泛型约束 type DiscriminatedUnion = { [K in keyof DiscriminatedMap]: DiscriminatedMap[K] & { kind: K } }[keyof DiscriminatedMap]
这个版本完全不需要显式声明泛型约束,TS会自动遍历DiscriminatedMap的所有键并生成对应的联合类型。如果后续需要针对单个kind生成类型,再单独写一个泛型版本即可。
不管用哪种方案,最终生成的类型结果和你原来的代码完全一致,只是写法更简洁高效了。
内容的提问来源于stack exchange,提问作者Daniel Birowsky Popeski
相关产品推荐
相关产品推荐

