Skip to content

Incorrect narrowing when a type guard returns a type union #41871

Description

TypeScript Version: 4.1.2

Search Terms:

narrowingnarrowedtype guardtype union

Code

MickeyPhoenix encountered this bug while implementing type guards for HBOMax data services. The original repro involves class inheritance, like this:

playground link

classAnimal{privatex: number=0;}classCatextendsAnimal{publicwhiskers: number=1;}classBroom{privatey: number=1;publicwhiskers: number=1;publichandle: number=3;}functionhasWhiskers(input: Animal|Broom): input is Cat|Broom{returninputinstanceofCat||inputinstanceofBroom;}functionincorrect(input: Animal|Broom): number{if(hasWhiskers(input)){// INCORRECT: There should be a compiler error here, // but `input` got incorrectly narrowed to `Broom` // (instead of `Cat | Broom`)returninput.handle;}return-1;}functioncorrect(input: Animal|Broom): number{if(inputinstanceofCat||inputinstanceofBroom){// CORRECT: `handle` is not a member of `Cat | Broom`,// so the compiler correctly reports an error here.returninput.handle;}return-1;}constcat=newCat();// Prints "undefined" because cat.handle didn't actually existconsole.log(incorrect(cat));

But it can also be reproduced using simpler interface inheritance, like this:

playground link

interfaceAnimal{legs: number;}interfaceCatextendsAnimal{whiskers: number;}interfaceBroom{whiskers: number;handle: number;}functionhasWhiskers(input: Animal|Broom): input is Cat|Broom{returnObject.hasOwnProperty.call(input,'whiskers');}functionincorrect(input: Animal|Broom): number{if(hasWhiskers(input)){// INCORRECT: There should be a compiler error here, // but `input` got incorrectly narrowed to `Broom` // (instead of `Cat | Broom`)returninput.handle;}return-1;}

Expected behavior:

One would expect identical type narrowing when extracting this:

functionf(input: Animal|Broom): number{if(inputinstanceofCat||inputinstanceofBroom){...

...into this:

functionhasWhiskers(input: Animal|Broom): input is Cat|Broom{returninputinstanceofCat||inputinstanceofBroom;}...functionf(input: Animal|Broom): number {if(hasWhiskers(input)){...

Actual behavior:

The type is incorrectly narrowed to Broom instead of Cat | Broom.

As a result, clearly incorrect code compiles without any error.

Related Issues:#31156

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions