TypeScript 4.9中枚举键泛型函数集合映射报错问题咨询
以下代码在TypeScript 4.8中可正常运行,目标是创建以MyEnum枚举值作为键的映射,每个键对应一个依赖枚举值的泛型函数集合:
enum MyEnum { A = 'my-a', B = 'my-b', } type MyEnumToObjectMap = { [MyEnum.A]: { a: string; }; [MyEnum.B]: { b: string; }; }; type MyGenericFunction<T extends MyEnum> = (data: MyEnumToObjectMap[T])=>void type Functions = { [key in MyEnum]: Set<MyGenericFunction<key>>; }; // Build the map const map = {} as Functions; for (const value of Object.values(MyEnum)) { map[value] = new Set<MyGenericFunction<typeof value>>(); } console.log(map);
但在TypeScript 4.9中运行时会出现如下类型错误:
S2322: Type 'Set
' is not assignable to type 'Set<MyGenericFunction<MyEnum.A>> & Set<MyGenericFunction<MyEnum.B>>'.
Types of property 'add' are incompatible.
Type '(value: MyGenericFunction) => Set' is not assignable to type '((value: MyGenericFunction<MyEnum.A>) => Set<MyGenericFunction<MyEnum.A>> & Set<MyGenericFunction<MyEnum.B>>) & ((value: MyGenericFunction<...>) => Set<...> & Set<...>)'.
Type '(value: MyGenericFunction) => Set' is not assignable to type '(value: MyGenericFunction<MyEnum.A>) => Set<MyGenericFunction<MyEnum.A>> & Set<MyGenericFunction<MyEnum.B>>'.
Types of parameters 'value' and 'value' are incompatible.
Types of parameters 'data' and 'data' are incompatible.
Type '{ a: string; } | { b: string; }' is not assignable to type '{ a: string; }'.
Property 'a' is missing in type '{ b: string; }' but required in type '{ a: string; }'.
错误原因
核心原因是TypeScript 4.9对循环中枚举值的类型推断逻辑进行了收紧:
- 在TypeScript 4.8中,遍历
Object.values(MyEnum)时,循环变量value会被逐个窄化为具体的枚举成员类型(MyEnum.A或MyEnum.B),因此MyGenericFunction<typeof value>会被实例化为对应成员的具体函数类型(比如(data: {a:string})=>void),与Functions类型中对应键的要求匹配。 - 但在TypeScript 4.9中,
Object.values(MyEnum)返回的类型被推断为MyEnum[](联合类型数组),循环变量value的类型始终是MyEnum(即MyEnum.A | MyEnum.B),而非单个成员类型。此时MyGenericFunction<typeof value>会被实例化为联合类型函数((data: {a:string})=>void) | ((data: {b:string})=>void)。 - 由于
Set的类型具有逆变特性,Set<联合类型函数>无法赋值给Set<具体成员函数>——前者的add方法允许添加任意一种联合类型的函数,而后者只允许添加对应具体成员的函数,TypeScript 4.9更严格地校验了这种类型不匹配,从而抛出错误。
内容的提问来源于stack exchange,提问作者Simon Tran

