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(
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!
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');
}
}
});
You also have this option:
window.addEventListener('OnRewards', (e: CustomEvent) => {
// your code here
} as (e: Event) => void)
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;
...
});