TypeScript v4.4.4 类型守卫执行后泛型未收窄如何修复?
问题根因
TypeScript 4.4 版本中,默认不会将非分布式的泛型类型G<A | B | C>自动识别为G<A> | G<B> | G<C>的联合类型,因此你自定义的类型守卫isA只能判断item是否为G<A>,但无法将剩余分支的类型自动收窄为G<B | C>。
修复方案
三种可行的修复方式如下:
方案1:将泛型定义改为分布式条件类型(推荐)
通过分布式条件类型让G<T>在接收联合类型入参时自动拆分为对应联合类型,类型守卫就可以正常执行收窄逻辑:
type Data = A | B | C // 改造G的定义为分布式条件类型 type G<T extends Data> = T extends unknown ? { type: 'a' | 'b' data: T } : never type A = { foo: string } type B = { bar: string } type C = { foobar: string } const isA = (item: G<Data>): item is G<A> => item.type === 'a' const throwOnA = (item: G<Data>): G<Exclude<Data, A>> => { if (!isA(item)) return item // 此处item会正确收窄为G<B | C> throw Error('is A') }
方案2:显式声明入参为联合类型
如果不想修改原有G<T>的定义,可以直接将函数入参声明为G<A> | G<B> | G<C>的联合类型,也可以触发正常的类型收窄:
type Data = A | B | C type G<T extends Data> = { type: 'a' | 'b' data: T } type A = { foo: string } type B = { bar: string } type C = { foobar: string } const isA = (item: G<Data>): item is G<A> => item.type === 'a' // 显式声明入参为联合类型 const throwOnA = (item: G<A> | G<B> | G<C>): G<Exclude<Data, A>> => { if (!isA(item)) return item // 类型收窄正常生效 throw Error('is A') }
方案3:临时加类型断言(应急方案)
如果不想修改类型定义,也可以在返回处手动添加类型断言跳过类型校验,适合小范围临时修复:
const throwOnA = (item: G<Data>): G<Exclude<Data, A>> => { if (!isA(item)) return item as G<Exclude<Data, A>> throw Error('is A') }
内容的提问来源于stack exchange,提问作者Serhii Shliakhov
相关产品推荐
相关产品推荐

