【问题标题】:Argument of type '(e: CustomEvent) => void' is not assignable to parameter of type 'EventListenerOrEventListenerObject'“(e:CustomEvent)=> void”类型的参数不可分配给“EventListenerOrEventListenerObject”类型的参数
【发布时间】:2022-04-07 20:14:37
【问题描述】:

我有这个自定义事件设置,它适用于 TypeScript 2.5.3,但是当我更新到 2.6.1 时出现错误

window.addEventListener('OnRewards', (e: CustomEvent) => {
    // my code here
})

[ts] '(e: CustomEvent) => void' 类型的参数不能分配给 'EventListenerOrEventListenerObject' 类型的参数。

类型 '(e: CustomEvent) => void' 不可分配给类型 'EventListenerObject'。

类型“(e:CustomEvent)=> void”中缺少属性“handleEvent”。

我不确定如何解决这个问题。

【问题讨论】:

  • 什么是CustomEvent?也许试试e: Event
  • CustomEventvar event = new CustomEvent('OnRewards', { detail: data });
  • 如果我使用Event那么它不知道detail在函数内部是什么
  • 也许他们在最新版本中引入了一个新错误?

标签: javascript typescript


【解决方案1】:

这是由于 TypeScript v2.6 中添加的 --strictFunctionTypes 编译器标志的行为所致。 (e: CustomEvent) => void 类型的函数不再被视为EventListener 的有效实例,它采用Event 参数,而不是CustomEvent

所以解决它的一种方法是关闭--strictFunctionTypes


另一种方法是传入一个函数,该函数接受 Event,然后通过类型保护缩小到 CustomEvent

function isCustomEvent(event: Event): event is CustomEvent {
  return 'detail' in event;
}

window.addEventListener('OnRewards', (e: Event) => {
  if (!isCustomEvent(e))
    throw new Error('not a custom event');
  // e is now narrowed to CustomEvent ...
  // my code here 
})

第三种方法是使用addEventListener()的另一个重载:

addEventListener<K extends keyof WindowEventMap>(type: K, listener: (this: Window, ev: WindowEventMap[K]) => any, useCapture?: boolean): void;

如果 type 参数是已知事件类型 (K extends keyof WindowEventMap) 的名称,例如 "onclick",则 listener 函数将期望其参数属于该缩小的事件类型 (WindowEventMap[K])。问题是 "OnRewards" 不是已知的事件类型...除非您使用 declaration merging使它已知:

// merge into WindowEventMap
interface WindowEventMap {
    OnRewards: CustomEvent
}

或者,如果您在模块中(任何带有export 的模块),请使用global augmentation

// merge into WindowEventMap
declare global {
  interface WindowEventMap {
    OnRewards: CustomEvent
  }
}

然后像以前一样使用您的代码:

// no error!
window.addEventListener('OnRewards', (e: CustomEvent) => {
    // my code here
})

所以,这些是您的选择。你想选择哪一个取决于你。希望有帮助;祝你好运!

【讨论】:

  • 我能够获得第二种工作方式,但是,我最喜欢第三种选择的方式。但它似乎不起作用,因为我仍然收到相同的错误消息。
  • 如果您的代码在模块内,您可能必须将合并包装在 declare global { ... } 中。
  • 太好了,刚刚编辑了答案,以防其他人遇到这个问题。
  • +1,但我真的希望你没有从 turn off --strictFunctionTypes 开始。这么多的 TS 问题都是用“禁用检查”来回答的,如果这是标准选择,那我们还不如放弃 TS。
  • 我们可以用if(!(event instanceof CustomEvent)) throw 'not a CustomEvent'检查而不是类型保护功能吗?
【解决方案2】:

jcalz's excellent answer 的基础上,您还可以使用type assertion 更简洁:

window.addEventListener('OnRewards', (e: Event) => {
    const detail = (<CustomEvent>e).detail;
    ...
});

【讨论】:

  • 我假设你的意思是e: Event
【解决方案3】:

你也有这个选项:

window.addEventListener('OnRewards', (e: CustomEvent) => {
    // your code here
} as (e: Event) => void)

【讨论】:

    【解决方案4】:

    我根据@jcalz 的回答创建了一个通用函数

    /**
     * Checks whether an object can be safely cast to its child type
     * @param parent the object to be 'narrowly' cast down to its child type
     * @param checkForProps props which aught to be present on the child type
     */
    export function isSubTypeWithProps<P, C extends P>(parent: P, ...checkForProps: (keyof C)[]): parent is C {
      return checkForProps.every(prop => prop in parent);
    }
    
    /**
     * Usage example
     */
    const el = document.getElementById('test');
    el.addEventListener('click', (e: Event) => {
      if (isSubTypeWithProps<Event, MouseEvent>(e, 'which')) {
        if (e.which === 1) { // primary mouse button only ('which' prop is only available on MouseEvent)
          console.log('clicked');
        }
      }
    });
    

    【讨论】:

      【解决方案5】:

      正如 Yukulélé 对 jcalz 的回答所评论的那样,您还可以检查 CustomEvent 是否是 Event 的实例。

      像下面这个例子:

      window.dispatchEvent(
        new CustomEvent('customEventName', {
          detail: { something: true },
        })
      );
      
      window.addEventListener('customEventName', (e: Event) => {
        if (e instanceof CustomEvent) {
          if (e?.detail?.something) {
            // do something
          }
        }
      });
      

      【讨论】:

        猜你喜欢
        • 2020-07-19
        • 1970-01-01
        • 1970-01-01
        • 2018-08-27
        • 2021-08-24
        • 2021-08-05
        • 1970-01-01
        • 2022-07-20
        • 2022-01-05
        相关资源
        最近更新 更多