Theo Itzaris
Theo Itzaris

Reputation: 4681

How to handle 'Event' type and 'CustomEvent' type on eventListeners, in Typescript

I m dispatching a CustomEvent with a detail property, in my app, and i grab it into another app, with an eventListener.

I m struggling with typescript to make it work smooth, but whatever i tried so far i get on an obstacle.

so the eventlistener is like so(all is happening into a react global component):

window.addEventListener('my-custom-event', this.callEvent);

And further down, i have the callEvent method like so:

  callEvent: any = (e: CustomEvent) => {
const {
  detail,
} = e;

.......
}

All the long error 'Typescript' messages are happening when i try to change the any type, to a meaningfull type, for the callEvent func, like:

Version 1:

callEvent: (e: CustomEvent) => void = (e: CustomEvent) => {...}

Version 2:.

callEvent(e: CustomEvent) {
const {
  detail,
} = e;

.....
}

Res:

No overload matches this call.
Overload 1 of 2, '(type: keyof WindowEventMap, listener: (this: Window, ev: Event | 
UIEvent | BeforeUnloadEvent | FocusEvent | MouseEvent | ... 21 more ... | 
PromiseRejectionEvent) => any, options?: boolean | ... 1 more ... | undefined): 
 void', gave the following error.
Argument of type '"my-custom-event"' is not assignable to parameter of type 
'keyof WindowEventMap'.
 Overload 2 of 2, '(type: string, listener: EventListenerOrEventListenerObject, 
 options?: boolean | AddEventListenerOptions | undefined): void', gave the following 
 error.
 Argument of type '(e: CustomEvent<any>) => void' is not assignable to parameter of 
 type 'EventListenerOrEventListenerObject'.
  Type '(e: CustomEvent<any>) => void' is not assignable to type 'EventListener'.
    Types of parameters 'e' and 'evt' are incompatible.
      Type 'Event' is missing the following properties from type 'CustomEvent<any>': 
 detail, initCustomEvent

Version 3:

callEvent: (e: Event) => void = (e: Event) => {
const detail = (<CustomEvent>e).detail; // it thinks this is JSX
...
}

Res:.

var CustomEvent: {
new <T>(typeArg: string, eventInitDict?: CustomEventInit<T> | undefined): 
 CustomEvent<T>;
prototype: CustomEvent<any>;
}
JSX element 'CustomEvent' has no corresponding closing tag.ts(17008)
'React' refers to a UMD global, but the current file is a module. Consider adding an 
import instead.ts(2686)
'CustomEvent' cannot be used as a JSX component.
Its instance type 'CustomEvent<unknown>' is not a valid JSX element.
Type 'CustomEvent<unknown>' is missing the following properties from type 
'ElementClass': render, context, setState, forceUpdate, and 3 more.

And some other versions but in vain. if you guys have any idea that i ll be able to replace, successfully, callEvent: any to a proper type , please give a shout.

It would also be helpful if there are any plugins or other editors that can identify types and suggest. Thanks.

Upvotes: 11

Views: 9203

Answers (2)

Kelvin Schoofs
Kelvin Schoofs

Reputation: 8718

It's because TypeScript doesn't know your custom event exists.

The first overload basically says it expects your event to be part of keyof WindowEventMap, which you can accomplish by augmenting WindowEventMap:

declare global {
    interface WindowEventMap {
        'my-custom-event': CustomEvent;
    }
}

// Won't error anymore (unless you misspell your event, of course)
window.addEventListener('my-custom-event', (event: CustomEvent) => {
    
});

The second overload mentions if you don't augment WindowEventMap, your argument needs to extend Event:

class MyCustomEvent extends Event {}

// No error either, even though 'my-other-custom-event' is completely new
window.addEventListener('my-other-custom-event', (event: MyCustomEvent) => {
    
});

Upvotes: 15

Theo Itzaris
Theo Itzaris

Reputation: 4681

Ok, it worked by adding a type cast on the eventListener, i share it for anyone who ll come up across with similar siuation.

So adding as (e: Event) => void at the end like so:

window.addEventListener('my-custom-event', this.callEvent as (e: Event) => void);

Upvotes: 4

Related Questions