如何基于TypeScript枚举创建映射并强制遗漏成员时报错?
问题
我需要做一个从ANIMAL_PLACE枚举到ANIMAL_TYPE枚举的映射对象,要求编译器在两种情况下报错:
- 漏写了
ANIMAL_PLACE里的任何成员 - 漏用了
ANIMAL_TYPE里的任何成员
现在的代码只能部分满足需求:
- 自定义的
AnimalPlaceToAnimalMapType既不检查ANIMAL_PLACE的遗漏,也不检查ANIMAL_TYPE的遗漏 - 用
Record<ANIMAL_PLACE, ANIMAL_TYPE[]>定义的对象,只会在漏写ANIMAL_PLACE成员时报错,管不了ANIMAL_TYPE的成员有没有全用上
代码示例:
export enum ANIMAL_TYPE { dog = "dog", cat = "cat", fish = "fish", foo = "foo" } export enum ANIMAL_PLACE { europe = 'europe', africa = 'africa', asia = "asia" } // 既不检测ANIMAL_PLACE遗漏,也不检测ANIMAL_TYPE遗漏 type AnimalPlaceToAnimalMapType = { [ANIMAL_PLACE.africa]: [ANIMAL_TYPE.cat, ANIMAL_TYPE.fish] [ANIMAL_PLACE.europe]: [ANIMAL_TYPE.dog] } // 只检测ANIMAL_PLACE成员遗漏 const AnimalPlaceToAnimalMapRecord: Record<ANIMAL_PLACE, ANIMAL_TYPE[]> = { [ANIMAL_PLACE.africa]: [ANIMAL_TYPE.cat, ANIMAL_TYPE.fish], [ANIMAL_PLACE.europe]: [ANIMAL_TYPE.dog], };
解决方案
1. 同时检测两种遗漏情况
要同时管住ANIMAL_PLACE和ANIMAL_TYPE的遗漏,可以写一个辅助类型来校验所有ANIMAL_TYPE成员是否都被包含在映射里,再结合Record来约束ANIMAL_PLACE的完整性:
先写辅助类型:
// 校验所有ANIMAL_TYPE成员都被覆盖的辅助类型 type AllAnimalTypesCovered<T extends Record<ANIMAL_PLACE, ANIMAL_TYPE[]>> = T[keyof T][number] extends infer AllUsedTypes ? ANIMAL_TYPE extends AllUsedTypes ? T : never : never;
然后用这个辅助类型约束最终的映射类型:
type AnimalPlaceToAnimalMap = AllAnimalTypesCovered<Record<ANIMAL_PLACE, ANIMAL_TYPE[]>>;
现在创建对象时,必须同时满足两个要求:
- 所有
ANIMAL_PLACE的成员都得写 - 所有
ANIMAL_TYPE的成员必须出现在至少一个数组里
比如正确的写法:
const AnimalPlaceToAnimalMap: AnimalPlaceToAnimalMap = { [ANIMAL_PLACE.africa]: [ANIMAL_TYPE.cat, ANIMAL_TYPE.fish], [ANIMAL_PLACE.europe]: [ANIMAL_TYPE.dog], [ANIMAL_PLACE.asia]: [ANIMAL_TYPE.foo] // 必须加foo,不然报错 };
漏写ANIMAL_PLACE.asia或者漏用ANIMAL_TYPE.foo,编译器都会直接报错。
2. 单独检测ANIMAL_TYPE遗漏(备选方案)
如果只需要确保ANIMAL_TYPE的成员都被用上,也可以用辅助函数来实现:
function createAnimalMap<T extends Record<ANIMAL_PLACE, ANIMAL_TYPE[]>>( map: T & AllAnimalTypesCovered<T> ): T { return map; }
用这个函数创建对象:
// 漏了ANIMAL_TYPE.foo的话直接报错 const AnimalPlaceToAnimalMap = createAnimalMap({ [ANIMAL_PLACE.africa]: [ANIMAL_TYPE.cat, ANIMAL_TYPE.fish], [ANIMAL_PLACE.europe]: [ANIMAL_TYPE.dog], [ANIMAL_PLACE.asia]: [ANIMAL_TYPE.foo] });
这个方式同样能强制所有ANIMAL_TYPE成员被使用,同时Record的约束也会保证ANIMAL_PLACE的成员不遗漏。
内容的提问来源于stack exchange,提问作者four-eyes
相关产品推荐
相关产品推荐

