Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Map enum values to individual types in TypeScript

I have a piece of TypeScript code like this:

enum EventType {
  EVENT_A = 'eventA',
  EVENT_B = 'eventB',
  ... // more event types
}

interface Event {
  type: EventType;
}

interface EventA extends Event {
  type: EventType.EVENT_A;
  data: PayloadForEventA;
}

interface EventB extends Event {
  type: EventType.EVENT_B;
  data: PayloadForEventB;
  id: number;
}

... // More Event interfaces

Is it possible to somehow map the values of EventType to corresponding interfaces? If it could not be done automatically, is it possible to specify the types mapped by the enum values manually?

I do know that it is possible to map values to types as shown in this question. And using the method shown below, it could create the lookup table I needed:

type EventTypeLut = {
  'eventA': EventA,
  'eventB': EventB,
  // ...
};

But this method hardcodes the value of enum into the LUT. The enum value might change in the future (which I don't have much control) and hardcoding the enum value makes it harder to maintain.

When I try to use EventType.EVENT_A as key name, TypeScript complains with "property or signature expected". Using template string seems also no avail. (It seems that the key name cannot be computed, even if it's a constant expression like 1+2.)

The reason I am trying to do this is I would like to create a typed EventEmitter with the enum value of EventType as name and respective typed object for callback function. If there is a better way to achieve this, please also give a hint. Thanks in advance.

like image 622
Daniel Lee Avatar asked May 23 '26 10:05

Daniel Lee


2 Answers

In addition to @chharvey 's answer, you could also ensure all of the EventType keys are included in the EventTypeLut by doing something like:

type EventTypeLut = {
  [T in EventType]: {
    [EventType.EVENT_A]: EventA,
    [EventType.EVENT_B]: EventB,
  }[T]
};

The [T in Event] will cause TypeScript to complain if not all of the enum members are provided.

like image 191
Max Kurapov Avatar answered May 25 '26 02:05

Max Kurapov


Object literals (and thus interfaces and object types) can have computed keys. For example:

const obj = {
  [2 + 2]: 'four'
};
obj[2 + 2] === 'four'; // true
obj[1 + 3] === 'four'; // true
obj[4]     === 'four'; // true
obj['4']   === 'four'; // true

Computed keys are expressions wrapped in brackets that can be used as keys of an object literal, interface, or object type. Try using computed keys for your EventTypeLut object type:

type EventTypeLut = {
  [EventType.EVENT_A]: EventA,
  [EventType.EVENT_B]: EventB,
  // ...
};
like image 20
chharvey Avatar answered May 25 '26 02:05

chharvey



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!