简体   繁体   English

尽管 IDE 正确推断了类型,为什么 Typescript 不推断泛型类型保护的类型?

[英]Why does Typescript not infer the type for a generic type guard although the IDE infers the type correctly?

Hello, world!

I am working with Typescript and Webstorm and I am trying to use a type guard, which does not seem to be totally correct.我正在使用 Typescript 和 Webstorm,我正在尝试使用类型保护,这似乎并不完全正确。

This is an example to show what I want to achieve.这是一个展示我想要实现的目标的例子。 It is also linked in a playground .它也与操场相连

 // This is a helper to find the matching class Type interface correspondsTo<Klass> {} // An interface for a plain javascript object interface WizardPojo extends correspondsTo<RealWizard>{ name?: string; } // A derivable abstract class abstract class Action { performAction() { console.log('Go!'); } } // A class which implements the plain interface and also inherits a method class RealWizard extends Action implements WizardPojo { name: string; performMagic() { console.log('✨'); } } // This is handled by typescript as expected function lameMagic () { const harry: RealWizard = new RealWizard(); harry.performMagic(); // correct harry.performAction(); // correct const harryPojo: WizardPojo = {}; harryPojo.performMagic() // error: 'performMagic' does not exist -> correct harryPojo.performAction() // error: 'performAction' does not exist -> correct } // Now here is something not working as expected function realMagic(maybe: WizardPojo | RealWizard ) { if(isKlassObject(maybe)) { // Why is that an error? maybe.performMagic(); maybe.performAction(); const wizard: RealWizard = maybe; // Why does not the type guard handle the cast? const asWizard = maybe as RealWizard; asWizard.performMagic(); asWizard.performAction() } } /* I guess this type guard needs some more magic to work correctly The weird thing: Webstorm (without Typescript language service setting set) recognizes the type correctly */ function isKlassObject<Pojo extends correspondsTo<any>> (obj: Pojo) : obj is Pojo extends correspondsTo<infer Klass> ? Klass : unknown { return typeof (obj as any).save === 'function'; }

The thing is: When I use Webstorm without the 'Typescript language service' activated, the type guard results in the correct type.问题是:当我在没有激活“Typescript 语言服务”的情况下使用 Webstorm 时,类型保护会产生正确的类型。 However, when I try to convert the code, tsc throws an error and I don't know why.但是,当我尝试转换代码时, tsc抛出错误,我不知道为什么。

In my project I have a lot of those Pojo interfaces and classes which are automatically generated in d.ts files and I want to use just one generic type guard function to determine if a Pojo is a Klass object.在我的项目中,我有很多在d.ts文件中自动生成的 Pojo 接口和类,我只想使用一个通用类型保护函数来确定 Pojo 是否是 Klass 对象。 And I want to avoid explicit type cast with as .而且我想避免使用as显式类型转换。

It is important to me to use both, Pojo interfaces and Klass types.对我来说同时使用 Pojo 接口和 Klass 类型很重要。

Do you have an idea, how to fix this type guard problem?你有什么想法,如何解决这个类型保护问题?

I found a solution 🎉🎉!我找到了解决方案🎉🎉!

I can use the same technique like sequelize uses it in its typings for the model attribute types .我可以使用与sequelize相同的技术,在模型属性类型的类型中使用它。

The trick is to assign the generic Klass type to a fake property in the correspondsTo interface.诀窍是将通用Klass类型分配给correspondsTo接口中的一个假属性。

Playground 操场

 // This is a helper to find the matching class Type interface correspondsTo<Klass> { /* * A dummy variable that doesn't exist on the real object. * This is needed so Typescript can infer the type correctly in the type guard. */ readonly _realKlass?: Klass; } // Use the dummy `_realKlass` attribute to infer the correct type 🎉 function isKlassObject<Pojo> (obj: Pojo) : obj is Pojo extends correspondsTo<any> ? Pojo['_realKlass'] : Pojo { return typeof (obj as any).performAction === 'function'; } // An interface for a plain javascript object interface WizardPojo extends correspondsTo<RealWizard>{ name?: string; } // A derivable abstract class abstract class Action { performAction() { console.log('Go!'); } } // A class which implements the plain interface and also inherits a method class RealWizard extends Action implements WizardPojo { name: string; performMagic() { console.log('✨'); } } // both type signatures are ok: either only `WizardPojo` function pojoMagic(maybe: WizardPojo ) { if(isKlassObject(maybe)) { // The type is inferred correctly! 🎉 maybe.performMagic(); maybe.performAction(); } } // ... or the intersection type `WizardPojo | RealWizard` function realMagic(maybe: WizardPojo | RealWizard ) { if(isKlassObject(maybe)) { // The type is inferred correctly! 🎉 maybe.performMagic(); maybe.performAction(); } }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM