如何实现满足特定约束的TypeScript泛型NewType工具类型?
泛型类型NewType的实现问题
需求说明
需要实现一个泛型类型NewType,接收字段名Field和可选值集合Params作为参数,需满足以下规则:
- 合法情况一:设置
Field字段为Params中的某一个值,此时所有Params对应的属性必须为never或undefined; - 合法情况二:设置
Params中的某一个属性为true,此时Field字段必须为never或undefined,且其余Params属性也必须为never或undefined; - 排除非法情况:同时设置
Field字段和某个Params属性为true,或设置多个Params属性为true。
预期用法示例
type AnimalExample = NewType<"animal","mammal"|"reptile"|"bird"> const example1:AnimalExample = {animal: "bird"} // 合法 const example2:AnimalExample = {animal: "bird", bird: true} // 非法 const example3:AnimalExample = {bird: true} // 合法 const example4:AnimalExample = {bird: true, reptile: true} // 非法
当前实现卡点
现有代码中,第二种合法情况的Partial<Record<Params, boolean>>无法实现分布式逻辑,无法限制只能选一个Params属性设为true:
type NewType<Field extends string, Params extends string> = | (Record<Field, Params> & Partial<Record<Params, never>>) | (Partial<Record<Field, undefined>> & Partial<Record<Params, boolean>>); // 此处需要实现分布式逻辑
已写出能生成单Params属性为true的联合类型的Test类型,但不知道如何整合进NewType:
type AnimalTypes = "mammal"|"reptile"|"bird" type Test<T> = T extends AnimalTypes ? { [Self in T]: true } & Partial< Record<Exclude<AnimalTypes, T>, never> > : never;
该Test类型生成的结果:
( | { mammal: true; reptile?: never; bird?: never } | { mammal?: never; reptile: true; bird?: never } | { mammal?: never; reptile?: never; bird: true } )
期望最终结果:
( | { animal: "mammal"|"reptile"|"bird"; mammal?: never; reptile?: never; bird?: never } | { mammal: true; reptile?: never; bird?: never; animal?: never } | { mammal?: never; reptile: true; bird?: never; animal?: never } | { mammal?: never; reptile?: never; bird: true; animal?: never } )
解决方案
把你写的Test类型的分布式逻辑泛化,整合进NewType的第二种情况即可:
type NewType<Field extends string, Params extends string> = // 合法情况一:设置Field字段,所有Params属性为never/undefined (Record<Field, Params> & Partial<Record<Params, never>>) // 合法情况二:设置单个Params属性为true,Field为never/undefined,其余Params属性为never/undefined | (Partial<Record<Field, undefined>> & { [P in Params]: { [Self in P]: true } & Partial<Record<Exclude<Params, P>, never>> }[Params]);
代码解释
- 合法情况一:保持原有逻辑,
Record<Field, Params>确保Field字段是Params中的值,Partial<Record<Params, never>>限制所有Params属性只能是never或undefined。 - 合法情况二:
Partial<Record<Field, undefined>>限制Field字段只能是undefined或不存在;- 通过
{ [P in Params]: ... }[Params]实现分布式联合:遍历Params中的每个类型P,生成一个仅P为true、其余Params属性为never/undefined的对象类型,最终这些类型组成联合类型,自然限制了只能选一个Params属性设为true。
测试该实现,完全符合预期用法的合法/非法判断,也能生成你期望的最终类型结构。
内容的提问来源于stack exchange,提问作者Julian Brooks
相关产品推荐
相关产品推荐

