Argument of type '(e: CustomEvent) => void' is not assignable to parameter of type 'EventListenerOrEventListenerObject'

前端 未结 4 1095
执笔经年
执笔经年 2021-02-07 04:11

I have this custom event setup, and it works with TypeScript 2.5.3, but when I updated to 2.6.1 I get an error

window.addEventListener(         


        
相关标签:
4条回答
  • 2021-02-07 04:51

    This is due to the behavior of the --strictFunctionTypes compiler flag added in TypeScript v2.6. A function of type (e: CustomEvent) => void is no longer considered to be a valid instance of EventListener, which takes an Event parameter, not a CustomEvent.

    So one way to fix it is to turn off --strictFunctionTypes.


    Another way is to pass in a function that takes an Event and then narrows to CustomEvent via a type guard:

    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 
    })
    

    A third way is to use the other overload of addEventListener():

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

    If the type parameter is the name of a known event type (K extends keyof WindowEventMap) like "onclick", then the listener function will expect its parameter to be of that narrowed event type (WindowEventMap[K]). The problem is that "OnRewards" is not a known event type... unless you use declaration merging to make it known:

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

    Or, if you're inside a module (anything with export in it), use global augmentation:

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

    Then use your code as before:

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

    So, those are your options. Which one you want to choose is up to you. Hope that helps; good luck!

    0 讨论(0)
  • 2021-02-07 04:52

    I created a generic function based off @jcalz's answer

    /**
     * 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');
        }
      }
    });
    
    0 讨论(0)
  • 2021-02-07 04:55

    You also have this option:

    window.addEventListener('OnRewards', (e: CustomEvent) => {
        // your code here
    } as (e: Event) => void)
    
    0 讨论(0)
  • 2021-02-07 04:57

    Building off of jcalz's excellent answer, you can also use a type assertion to be a little cleaner:

    window.addEventListener('OnRewards', (e: Event) => {
        const detail = (<CustomEvent>e).detail;
        ...
    });
    
    0 讨论(0)
提交回复
热议问题