TypeScript能否通过带推断泛型的参数函数收窄可辨识联合类型?
1. 可行实现方案
你的代码首先存在笔误:narrowingFunction的第一个入参thing的类型写为了Thing,实际应该是联合类型Things。
目前有两种可正常运行的实现方案:
方案1:使用类型断言
你已经通过逻辑判断确保了thing的key和传入的key一致,符合NarrowedThing<Things, K>的类型要求,直接加类型断言即可:
interface Thing { key: 'bar' property: number } interface OtherThing { key: 'baz' property: string } type Things = Thing | OtherThing type NarrowedThing<A, K> = A extends { 'key': K } ? A : never const narrowingFunction = <K extends Things['key']>(thing: Things, key: K, callback: (thing: NarrowedThing<Things, K>) => void) => { if(thing.key === key) { callback(thing as NarrowedThing<Things, K>) } }
方案2:自定义类型守卫
把判断逻辑抽为自定义类型守卫,让TS可以识别收窄逻辑:
function isMatchingThing<K extends Things['key']>(thing: Things, key: K): thing is NarrowedThing<Things, K> { return thing.key === key } const narrowingFunction = <K extends Things['key']>(thing: Things, key: K, callback: (thing: NarrowedThing<Things, K>) => void) => { if(isMatchingThing(thing, key)) { callback(thing) // 无类型报错 } }
2. 原始代码报错的底层原因
该功能完全可以实现,原始报错的核心原因有两个:
- TS的控制流分析目前不支持对未绑定具体类型的泛型参数做关联收窄。泛型
K在函数定义阶段还没有确定具体的字面量类型,TS没办法提前推断出thing.key === key这个判断对应的类型收缩规则,它只会单独识别thing是Things联合类型,不会把它和泛型K的约束关联起来。 - TS的类型系统采用结构化类型设计,且泛型会在编译期擦除,运行时不存在泛型信息。如果
K是联合类型(比如同时传入'bar'|'baz'作为key的类型),就算两个值相等,也没办法提前确定要收窄到哪一个具体的子类型,为了保证类型安全,TS默认不会做这种不确定的自动收窄。
内容的提问来源于stack exchange,提问作者Ben Wainwright
相关产品推荐
相关产品推荐

