Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript keyboard event : argument of type 'Event' is not assignable to parameter of type 'KeyboardEvent'

I'have the following error even if the code run perfectly:

"TS2345: Argument of type 'Event' is not assignable to parameter of type 'KeyboardEvent'.
  Property 'altKey' is missing in type 'Event'." 

// In a Class

public listenTo = (window: Window) => {
  ['keydown', 'keyup'].forEach(eventName => {
     window.addEventListener(eventName, e => {
       this.handleEvent(e); // <- error here
     });
   });
}

public handleEvent = (event: KeyboardEvent) => {
    const { key } = event;
    //...
}

I tried to define the event type to KeyboardEvent, but I have the following error :

 window.addEventListener(eventName, (e:KeyboardEvent) => {
           this.handleEvent(e); // <- error here
         });


 TS2345: Argument of type '(event: KeyboardEvent) => void' is not assignable to parameter of type 'EventListenerOrEventListenerObject'.
  Type '(event: KeyboardEvent) => void' is not assignable to type 'EventListenerObject'.
 Property 'handleEvent' is missing in type '(event: KeyboardEvent) => void'.

Is there a way to pass through or resolve the issue ?

like image 400
Fabrice Avatar asked Nov 13 '17 10:11

Fabrice


2 Answers

TypeScript isn't able to make the full leap here, because all it knows is the event name will be a string, so the most general event type is used.

Examples below converted to a stand alone running example - so I've taken things "out of a class" for the demo...

While the strings are keydown and keyup you can guarantee the type safety, and overrule the compiler:

let listenTo = (window: Window) => {
  ['keydown', 'keyup'].forEach(eventName => {
     window.addEventListener(eventName, e => {
       handleEvent(<any>e);
     });
   });
}

let handleEvent = (event: KeyboardEvent) => {
    const { key } = event;
    //...
}

This would fall down if some other string was added to your array of event names.

Full type safety is available when using a string directly, due to specialized signatures:

  window.addEventListener('keydown', e => {
     handleEvent(e); // e is KeyboardEvent
  });

So you could type your array more strongly to get the correct types goodness:

type KeyboardEventNames = 'keydown' | 'keyup';

let listenTo = (window: Window) => {
  const eventNames: KeyboardEventNames[] = ['keydown', 'keyup']; 
  eventNames.forEach(eventName => {
     window.addEventListener(eventName, e => {
       handleEvent(e);
     });
  });
}

let handleEvent = (event: KeyboardEvent) => {
    const { key } = event;
    //...
}

In this last example, we restict the type of elements in the array to only keyboard event names, so the compiler now knows it isn't dealing with just any old string, and can infer the event type.

like image 54
Fenton Avatar answered Nov 17 '22 14:11

Fenton


The strings 'keyup' and 'keydown' are already known string literal types. However, strings in your code are not candidates for checking against known string literal types unless they are const. Simply make yours constant:

public listenTo = (window: Window) => {
  ['keydown' as const, 'keyup' as const].forEach(eventName => {
     window.addEventListener(eventName, e => {
       this.handleEvent(e);
     });
   });
}

or

public listenTo = (window: Window) => {
  (['keydown', 'keyup'] as const).forEach(eventName => {
     window.addEventListener(eventName, e => {
       this.handleEvent(e);
     });
   });
}

depending on your tastes.

like image 33
John C Avatar answered Nov 17 '22 16:11

John C