TypeScript泛型函数按入参返回映射类型值时报类型不兼容错误
问题描述
- 实现泛型函数时,需要根据传入参数返回对应映射类型的取值,函数调用侧类型推断完全正常,但函数内部
return语句抛出类型不兼容错误 - 实际使用场景中枚举
E的取值超过100个,不接受为genericFunction编写函数重载的方案
问题复现代码:
enum E { e1="e1", e2="e2" } interface E1Payload { payloadFrom1: string anotherField: number } interface E2Payload { payloadFrom2: string } type Mapping = { [E.e1]: E1Payload [E.e2]: E2Payload } function genericFunction<K extends keyof Mapping>(e: K): Mapping[K] { if (e === E.e1) { // 报错: Type '{ payloadFrom1: string; anotherField: number; }' is not assignable to type 'Mapping[K]'. return { payloadFrom1: "e1", anotherField: 2 } } // 报错: Type '{ payloadFrom2: string; }' is not assignable to type 'Mapping[K]'. return { payloadFrom2: "e2" } } // 调用侧类型推断完全正常 const payloadE1: E1Payload = genericFunction(E.e1) const payloadE2: E2Payload = genericFunction(E.e2)
报错原因
TypeScript的控制流分析不会收窄泛型类型参数本身:当你通过if (e === E.e1)做判断时,只会收窄入参e的类型,不会将泛型参数K收窄为具体的E.e1类型。此时TS会认为返回值必须满足所有可能K对应的返回值类型的交叉类型(即E1Payload & E2Payload),自然会报属性缺失的类型错误。
解决方案
提供两种无需编写函数重载的实现方案,适配大数量枚举的场景:
方案1:类型断言(写法最简洁)
当你已经通过分支逻辑保证了返回值和入参的对应关系时,可以直接对返回值做类型断言,跳过TS的内部类型检查:function genericFunction<K extends keyof Mapping>(e: K): Mapping[K] { if (e === E.e1) { return { payloadFrom1: "e1", anotherField: 2 } as Mapping[K] } return { payloadFrom2: "e2" } as Mapping[K] }方案2:预先构造映射表(类型更安全,推荐)
直接按照Mapping类型定义实现层的映射常量,函数内部直接从映射表取值,完全避开分支收窄的问题,不需要任何类型断言。后续给Mapping新增成员时,TS会直接在映射表定义处提示缺失字段,维护性更好:const mappingImpl: Mapping = { [E.e1]: { payloadFrom1: "e1", anotherField: 2 }, [E.e2]: { payloadFrom2: "e2" } } function genericFunction<K extends keyof Mapping>(e: K): Mapping[K] { return mappingImpl[e] }
两种方案都不会影响原有调用侧的类型推断效果。
内容的提问来源于stack exchange,提问作者ie8888
相关产品推荐
相关产品推荐

