Typescript: Discriminated switch on generic enum type not inferring correct type

Created on 20 Apr 2018  路  6Comments  路  Source: microsoft/TypeScript

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

Duplicate

Most helpful comment

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.

All 6 comments

Related (I think): #21483

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 }

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?

The generic type is the correct way to do it.. the type really should be T extends MyEnum.One and not T extends MyEnum. https://github.com/Microsoft/TypeScript/pull/22348 should address that.

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.

Tracking at #22348

Was this page helpful?
0 / 5 - 0 ratings

Related issues

manekinekko picture manekinekko  路  3Comments

seanzer picture seanzer  路  3Comments

blendsdk picture blendsdk  路  3Comments

bgrieder picture bgrieder  路  3Comments

DanielRosenwasser picture DanielRosenwasser  路  3Comments