'How to correctly type generic function with two parameters that depend on the generic?

I have a function onEvent as follows:

interface Events {
  'finished': {
    input: number;
    result: number;
  };
  'error': {
    input: number;
    error: Error;
  };
}

const onEvent = <K extends keyof Events>(eventLabel: K, eventDetails: Events[K]) => {
  if (eventLabel === 'finished') {
    console.log(eventDetails.input); // works, probably because 'input' is defined on both events.
    console.log(eventDetails.result); // error ts2339: Property 'result' does not exist...
  }
};

I would have expected that within the if clause, eventLabel is narrowed to 'finished', from which it is inferred that eventDetails can be narrowed to {input: number; result: number}, so that eventDetails.result is defined.

But that does not happen; the type of eventDetails is rather just Events[keyof Events]. Why is that and what can I do to get the typings right?

Playground Link



Solution 1:[1]

After some trying out, I found a solution. I cannot imagine it's the "canonical" one because it's a bit indirect, but it works:

const onEvent: (...args: {[k in keyof Events]: [k, Events[k]]}[keyof Events]) => void =
  (eventLabel, eventDetails) => {
    if (eventLabel === 'finished') {
      console.log(eventDetails.input); // works, probably because 'input' is defined on both events.
      console.log(eventDetails.result); // works now too
    }
};

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Remirror