TypeScript泛型场景类型收窄丢失:Test1失败Test2成功原因及解决
问题解析与解决方案
为什么test1失败、test2成功?
- test1是泛型函数:泛型参数
T是TableType的子类型,但TypeScript的控制流分析无法对泛型参数做精确收窄。哪怕进入了source === TableType.Shipment的分支,类型系统依然认为T可能是整个TableType联合类型(比如调用时传入TableType而非单个枚举值),所以Test<typeof source>还是原始的条件类型T extends TableType.Shipment ? number : string。此时无论是赋值0还是""都不符合这个不确定的条件类型,因此两者都报错。 - test2是普通函数:参数
source是具体的TableType联合类型,TypeScript的控制流分析能在if分支里把source的类型精确收窄为TableType.Shipment,所以Test<typeof source>直接解析为number,赋值0符合类型要求、赋值""不符合,结果完全符合预期。
不删除泛型让test1通过的方法
有几种可行的实现方式:
方式1:用类型断言缩小范围
在分支里把source断言为具体的TableType.Shipment,让类型系统明确当前的精确类型:
function test1<T extends TableType>(source: T) { if (source === TableType.Shipment) { const shipmentSource = source as TableType.Shipment; const ret: Test<typeof shipmentSource> = 0; // 成功 // const ret1: Test<typeof shipmentSource> = ""; // 失败,符合预期 } }
方式2:自定义类型守卫函数
通过类型守卫告诉TypeScript,当条件成立时,source的类型是T与TableType.Shipment的交集,从而精确收窄泛型T的范围:
function isShipment<T extends TableType>(source: T): source is T & TableType.Shipment { return source === TableType.Shipment; } function test1<T extends TableType>(source: T) { if (isShipment(source)) { const ret: Test<typeof source> = 0; // 成功 // const ret1: Test<typeof source> = ""; // 失败,符合预期 } }
方式3:利用const断言强制类型收窄
直接在分支里对source做const断言,强制类型系统识别其具体值类型:
function test1<T extends TableType>(source: T) { if (source === TableType.Shipment) { const ret: Test<typeof (source as const)> = 0; // 成功 } }
内容的提问来源于stack exchange,提问作者Athe
相关产品推荐
相关产品推荐

