为什么TypeScript无法推断泛型类型守卫类型,IDE却可以?如何修复?
问题根源
你遇到的tsc编译报错、Webstorm和tsc表现不一致的核心原因有两个:
- TypeScript是结构类型系统,你定义的
correspondsTo是空接口,任意类型结构上都符合这个接口的约束。当你传入WizardPojo | RealWizard联合类型作为入参时,RealWizard没有显式继承correspondsTo接口,条件类型Pojo extends correspondsTo<infer Klass> ? Klass : unknown对RealWizard分支的判断会走false,返回unknown,最终整个类型守卫的返回类型被推导为RealWizard | unknown也就是unknown,自然无法完成类型收窄。 - Webstorm关闭TypeScript语言服务后使用的是自研的类型推导逻辑,没有严格遵循TypeScript官方的分布式条件类型推导规则,所以才会出现识别正确的假象,实际编译行为以tsc为准。
修复方案
你可以按下面的方式调整代码,即可实现通用的类型守卫,同时不需要修改原有Pojo接口和Klass类的使用逻辑:
// 新增唯一标记,避免空接口的结构兼容问题 declare const __klassTag: unique symbol; interface correspondsTo<Klass> { [__klassTag]?: Klass; } // 原有Pojo接口定义无需修改 interface WizardPojo extends correspondsTo<RealWizard>{ name?: string; } abstract class Action { performAction() { console.log('Go!'); } } // 原有类定义无需修改 class RealWizard extends Action implements WizardPojo { name: string; performMagic() { console.log('✨'); } } // 调整后的通用类型守卫 function isKlassObject<Pojo extends correspondsTo<any>>( obj: Pojo | (Pojo extends correspondsTo<infer Klass> ? Klass : never) ): obj is Pojo extends correspondsTo<infer Klass> ? Klass : never { // 保留你原有的业务判断逻辑 return typeof (obj as any)?.save === 'function'; } // 测试验证 function realMagic(maybe: WizardPojo | RealWizard ) { if(isKlassObject(maybe)) { // 此时maybe已被正确收窄为RealWizard类型,编译不会报错 maybe.performMagic(); maybe.performAction(); const wizard: RealWizard = maybe; } }
内容的提问来源于stack exchange,提问作者Jupiter
相关产品推荐
相关产品推荐

