Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Does not infer discriminated unions with !== #32748

Closed
ChuckJonas opened this issue Aug 6, 2019 · 3 comments
Closed

Does not infer discriminated unions with !== #32748

ChuckJonas opened this issue Aug 6, 2019 · 3 comments
Assignees
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@ChuckJonas
Copy link

ChuckJonas commented Aug 6, 2019

TypeScript Version: v3.5.1

Search Terms:
discriminated unions, !==
Code

export interface K9 {
  variant: 'DOG' | 'WOLF';
  bark: ()=>void;
}

export interface Feline {
  variant: 'CAT' | 'LION';
  meow: ()=>void;
}

export type CatOrDog = K9 | Feline;

let instance = {} as any as (K9 | Feline)

if (instance.variant === 'DOG' || instance.variant === 'WOLF') {
    instance.bark(); //works
}

if (instance.variant !== 'DOG' && instance.variant !== 'WOLF') {
    let x = instance.variant; //correct type inferred
    instance.meow() //should work but doesn't
}

Expected behavior:

The first condition check should be able to infer that we are dealing with a Feline.

Actual behavior:
It is unable to discriminate.

Playground Link: playground link

Related Issues: #27541

@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label Aug 7, 2019
@RyanCavanaugh RyanCavanaugh added this to the TypeScript 3.7.0 milestone Aug 7, 2019
@jack-williams
Copy link
Collaborator

What's the difference between this and #30557?

@RyanCavanaugh
Copy link
Member

@jack-williams here, the narrowed identifier was declared as a union

@ahejlsberg
Copy link
Member

It works if you change the declaration of K9 to

export interface Dog {
  variant: 'DOG';
  bark: ()=>void;
}

export interface Wolf {
  variant: 'WOLF';
  bark: ()=>void;
}

export type K9 = Dog | Wolf;

The reason it doesn't work as originally written is that when instance.variant !== 'DOG' is applied to the original declaration of K9, no narrowing is performed (because variant could still be 'WOLF'). Likewise, instance.variant !== 'WOLF' doesn't by itself narrow instance. However, when K9 is declared as a union type we can individually narrow.

So, it's working as intended, but you could argue it is a design limitation.

@ahejlsberg ahejlsberg added Design Limitation Constraints of the existing architecture prevent this from being fixed and removed Bug A bug in TypeScript labels Oct 30, 2019
@ahejlsberg ahejlsberg removed this from the TypeScript 3.8.0 milestone Oct 30, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

4 participants