Karta enum-värden till individuella typer i TypeScript


Jag har en bit TypeScript-kod som ser ut så här:

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

Är det möjligt att på något sätt koppla värdena i EventType till motsvarande gränssnitt? Om det inte går att göra automatiskt, är det möjligt att specificera typerna som kopplas till enum-värdena manuellt?

Jag vet att det är möjligt att mappa värden till typer som visas i den här frågan. Och genom att använda metoden som visas nedan, skulle det kunna skapa uppslags-tabellen jag behövde:

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

Men denna metod hårdkodar värdet av enum i LUT:en. Enum-värdet kan ändras i framtiden (vilket jag inte har mycket kontroll över) och att hårdkoda enum-värdet gör det svårare att underhålla.

När jag försöker använda EventType.EVENT_A som nyckelnamn, klagar TypeScript med "egenskap eller signatur förväntad". Att använda en template-sträng verkar inte heller fungera. (Det verkar som att nyckelnamnet inte kan beräknas, även om det är ett konstant uttryck som 1+2.)

Anledningen till att jag försöker göra detta är att jag vill skapa en typad EventEmitter med enum-värdet av EventType som namn och respektive typat objekt för callback-funktionen. Om det finns ett bättre sätt att uppnå detta, vänligen ge också en ledtråd. Tack på förhand.

Svar #1

Förutom @chharvey:s svar, kan du också säkerställa att alla EventType-nycklar inkluderas i EventTypeLut genom att göra något som:

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

The [T in Event] kommer få TypeScript att klaga om inte alla enum-medlemmar anges.

Svar #2

Objektliteraler (och därmed gränssnitt och objekttyper) kan ha beräknade nycklar. Till exempel:

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

Beräknade nycklar är uttryck inneslutna i parenteser som kan användas som nycklar i ett objektliteralt, interface eller objekttyp. Försök använda beräknade nycklar för din EventTypeLut objekttyp:

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

Relaterade artiklar