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
TypeScript Version: 4.1.2
Search Terms:
narrowingnarrowedtype guardtype unionCode
MickeyPhoenix encountered this bug while implementing type guards for HBOMax data services. The original repro involves
classinheritance, like this:playground link
But it can also be reproduced using simpler
interfaceinheritance, like this:playground link
Expected behavior:
One would expect identical type narrowing when extracting this:
...into this:
Actual behavior:
The type is incorrectly narrowed to
Broominstead ofCat | Broom.As a result, clearly incorrect code compiles without any error.
Related Issues:#31156