Typescript键盘事件:'Event'类型的参数不能分配给'KeyboardEvent'类型的参数

时间:2017-11-13 10:02:29

标签: javascript typescript casting type-conversion

即使代码运行完好,我也会出现以下错误:

"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;
    //...
}

我尝试将事件类型定义为KeyboardEvent,但是我有以下错误:

 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'.

有没有办法通过或解决问题?

2 个答案:

答案 0 :(得分:2)

TypeScript无法在这里实现全面飞跃,因为它只知道事件名称将是一个字符串,因此使用了最常用的事件类型。

下面的例子转换为一个独立的运行示例 - 所以我已经采取了一些东西&#34;一个类&#34;为了演示...

虽然字符串是keydownkeyup,但您可以保证类型安全,并取代编译器:

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

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

如果将某个其他字符串添加到您的事件名称数组中,这将会失败。

由于专业签名,直接使用字符串时可以使用完全类型安全:

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

因此,您可以更强烈地键入数组以获得正确的类型:

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;
    //...
}

在最后一个例子中,我们将数组中的元素类型仅仅重置为键盘事件名称,因此编译器现在知道它不会处理任何旧字符串,并且可以推断出事件类型。

答案 1 :(得分:1)

字符串'keyup''keydown'是已知的字符串文字类型。但是,代码中的字符串不是用于检查已知字符串文字类型的候选对象,除非它们是const。只需使您的常数不变:

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

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

取决于您的口味。