Вывод типа аргумента функции на основе другого аргумента с дискриминированным объединением в TypeScript - PullRequest
2 голосов
/ 03 мая 2020

У меня есть дискриминируемый союз, описывающий тип события. Тогда у меня есть addEventHandler функция, которая принимает идентификатор события в качестве первого аргумента и обратный вызов события в качестве второго. Поле идентификатора события является дискриминатором типа объединения.

interface TestEventA {
  id: 'TestEventA';
  payload: null;
}

interface TestEventB {
  id: 'TestEventB';
  payload: TestEventBPayload;
}
interface TestEventBPayload {
  content: string;
}

interface TestEventC {
  id: 'TestEventC';
  payload: TestEventCPayload;
}
interface TestEventCPayload {
  value: number;
}

type TestEvent = (TestEventA | TestEventB | TestEventC);

function addEventHandler<T extends TestEvent['id']>(eventId: T, listener: (data: (TestEvent & { id: T })) => void) {
  eventEmitter.on(eventId, listener);
}


addEventHandler('TestEventC', (event) => {
  // I want event to be of type TestEventC, but instead it is of type
  // (TestEventA & { id: "TestEventC"; }) | (TestEventB & { id: "TestEventC"; }) | (TestEventC & { id: "TestEventC"; })

  // event.payload: Object is possibly 'null'
  // Property 'value' does not exist on type 'TestEventBPayload | TestEventCPayload'
  console.log(event.payload.value);

  if (event.id === 'TestEventC') { // redundant condition
    console.log(event.payload.value); // No error here
  }
});

Как настроить addEventHandler функцию обратного вызова для определения типа события?

1 Ответ

0 голосов
/ 03 мая 2020

В качестве обходного пути вы можете создать интерфейс, который сопоставляет идентификаторы событий с полезными нагрузками, например:

interface TestEventBPayload {
  content: string;
}

interface TestEventCPayload {
  value: number;
}

interface Events {
  TestEventA: null,
  TestEventB: TestEventBPayload,
  TestEventC: TestEventCPayload
};

function addEventHandler<TId extends keyof Events>(
  eventId: TId,
  listener: (data: Events[TId] & { id: TId }) => void
): void {
  eventEmitter.on(eventId, listener);
}

addEventHandler('TestEventC', (event) => {
  console.log(event.value);

  if (event.id === 'TestEventC') {
    console.log(event.value);
  }
});

Playground

...