Home > Enterprise >  Hint for generic type seems wrong in the playground?
Hint for generic type seems wrong in the playground?

Time:12-20

Consider the following code:

interface MyMouseEvent {
  x: number;
  y: number;
}

interface MyKeyboardEvent {
  key: string;
}

interface MyEventObjects {
  click: MyMouseEvent;
  keypress: MyKeyboardEvent;
}

function handleEvent<K extends keyof MyEventObjects>(
  eventName: K,
  callback: (e: MyEventObjects[K]) => void
) {
  
}

handleEvent();
handleEvent('click', (e: MyMouseEvent) => {});

Error popup for the call with no args says callback is (e: MyMouseEvent | MyKeyboardEvent) => void. But by my understanding of covariance if that is the correct type for callback, that second call should fail. But it doesn't. And indeed, it seems like the popup is wrong:

function test(f: (e: MyMouseEvent | MyKeyboardEvent) => void): void {}
test('click', (e: MyMouseEvent) => {});

The call to test does fail, and fails with exactly the message I would expect. So my understanding is that the popup for callback in the no argument call to handleEvent is actually (e: MyMouseEvent) => void | (e: MyKeyboardEvent) => void. Is this correct?

Playground

CodePudding user response:

Yes, your understanding is correct. In the function signature for handleEvent, the type parameter K is constrained to be a key of MyEventObjects, which means that it can only be either 'click' or 'keypress'. Therefore, the type of the callback parameter will be either (e: MyMouseEvent) => void or (e: MyKeyboardEvent) => void, depending on the value of eventName.

In the call to handleEvent with no arguments, the compiler does not have enough information to infer the type of K, and so it defaults to the least restrictive type, which in this case is the union of 'click' and 'keypress'. This means that the type of the callback parameter in this case will be the union of the two possible types, (e: MyMouseEvent | MyKeyboardEvent) => void

On the other hand, in the call to test, the function signature specifies that the f parameter has type (e: MyMouseEvent | MyKeyboardEvent) => void, which means that the callback passed to test must be able to handle events of either type. In this case, the callback passed as an argument has type (e: MyMouseEvent) => void, which is not a subtype of (e: MyMouseEvent | MyKeyboardEvent) => void, so the call fails with an error.

  • Related