简体   繁体   中英

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.

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. However, when I try to convert the code, tsc throws an error and I don't know why.

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. And I want to avoid explicit type cast with as .

It is important to me to use both, Pojo interfaces and Klass types.

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 .

The trick is to assign the generic Klass type to a fake property in the correspondsTo interface.

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(); } }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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