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

Discriminated switch on generic enum type not inferring correct type #23578

Closed
oleg-codaio opened this issue Apr 20, 2018 · 6 comments
Closed
Labels
Duplicate An existing issue was already created

Comments

@oleg-codaio
Copy link

TypeScript Version: 2.9.0-dev.20180420

Search Terms: discriminated generic enum switch

Code

enum MyEnum {
  ONE = 'one',
  TWO = 'two',
}

interface TypeMap {
  [MyEnum.ONE]: number;
  [MyEnum.TWO]: string;
}

function doAction<T extends MyEnum>(type: T, value: TypeMap[T]): void {
  switch (type) {
    case MyEnum.ONE:
      console.log('type:', type, ', value:', value);
      break;
    case MyEnum.TWO:
      console.log('type:', type, ', value:', value);
      break;
    default:
      console.log('type:', type, ', value:', value);
      break;
  }
}

Expected behavior: the types of type would correspond to MyEnum.ONE / MyEnum.TWO depending on the case, and thus value would also get the expected type.

Actual behavior:: type is still T extends MyEnum even within the discriminated switch statement.

Playground Link: TS Playground

@jack-williams
Copy link
Collaborator

Related (I think): #21483

@mhegazy
Copy link
Contributor

mhegazy commented Jul 18, 2018

T extends MyEnum is the right type. T is still generic, and in the presence of intersection type, a type can be manufactured that is more specific than MyEnum to instantiate your function with, e.g. MyEnum.One & { tag: void }

@mhegazy mhegazy added the Working as Intended The behavior described is the intended behavior; this is not a bug label Jul 18, 2018
@oleg-codaio
Copy link
Author

Thanks for getting back, that does make sense. How would you rewrite the code to support this scenario, though? I tried something like this:

function doAction(type: MyEnum, value: TypeMap[typeof type]): void {
  switch (type) {
    case MyEnum.ONE:
      console.log('type:', type, ', value:', value);
      break;
    case MyEnum.TWO:
      console.log('type:', type, ', value:', value);
      break;
    default:
      console.log('type:', type, ', value:', value);
      break;
  }
}

but of course, in this case value is just going the be the union of all the types of values in TypeMap (number | string), so that doesn't work. Would we need something like T extendsExactlyAndNothingMore MyEnum?

@mhegazy
Copy link
Contributor

mhegazy commented Jul 18, 2018

The generic type is the correct way to do it.. the type really should be T extends MyEnum.One and not T extends MyEnum. #22348 should address that.

@mhegazy mhegazy added Bug A bug in TypeScript and removed Working as Intended The behavior described is the intended behavior; this is not a bug labels Jul 18, 2018
@mhegazy mhegazy added this to the TypeScript 3.1 milestone Jul 18, 2018
@oleg-codaio
Copy link
Author

As a workaround, the following seems to now work with 3.3 (referring to the original):

function doAction<T extends MyEnum>(type: T & MyEnum, value: TypeMap[T]): void {

The type within a switch case gets narrowed down to something like T & MyEnum.ONE.

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Mar 13, 2019
@RyanCavanaugh
Copy link
Member

Tracking at #22348

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

6 participants