-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add type to retrieve valid events of an EventTarget #33047
Comments
An alternative solution for the meantime for anyone interested: export type EventMap<T> = T extends Window
? WindowEventMap
: (T extends Document ? DocumentEventMap : { [key: string]: Event });
export function managedEventListener<
T extends EventTarget,
K extends keyof EventMap<T> & string
>(
target: T,
type: K,
callback: (this: T, ev: EventMap<T>[K]) => any,
options?: AddEventListenerOptions,
) {
target.addEventListener(type, callback as EventListener, options);
return () => {
target.removeEventListener(type, callback as EventListener, options);
};
} |
I've been looking for this feature as well, although I'd prefer if there were also a way to declare valid event types and names in a given interface (imagine creating interfaces for design systems that might have multiple implementations in web components, Angular, etc.). I'm not a TypeScript expert by any means, but something like the following interface AccordionToggle extends CustomEvent {
detail: Boolean;
}
interface Accordion {
open: Boolean;
toggle(): Boolean;
[EventMap] 'accordion-toggle': AccordionToggle;
}
class XAccordion extends HTMLElement implements Accordion {
open: Boolean;
toggle() {
this.open = !this.open;
const toggleEvent: AccordionToggle = new CustomEvent('accordion-toggle', {
composed: true,
detail: this.open
});
this.dispatchEvent(toggleEvent);
return this.open;
}
} Since TypeScript can now work with JSDocs, I'd hope this would be analogous to the @fires tag described there and would interop with that feature. |
Also, Here's an example: type SpellCastEvent = CustomEvent<{
id: number;
effect: string;
name: string;
}> & {
type: 'spell_cast';
};
type SpellcasterEventMap = {
spell_cast: SpellCastEvent;
};
class Spellcaster extends EventTarget<SpellcasterEventMap> {
constructor() {
super();
// ...
}
}
function spellEventHandler (event: SpellCastEvent) {
console.log(`The ${event.detail.name} spell was cast.`);
}
const spellcaster = new Spellcaster();
spellcaster.addEventListener('spell_cast', spellEventHandler); Right now, this causes an error when calling function spellEventHandler (event: Event) {
const ev = (event as SpellCastEvent);
console.log(`The ${ev.detail.name} spell was cast.`);
} You can see the errors I'm describing in #39425. |
The first solution coming to my mind is related to #26591 type EventTypes<El extends EventTarget> = Parameters<El["addEventListener"]>[0]; Yet there's also the fact that type EventMap<T extends EventTarget> = T extends MediaQueryList
? MediaQueryListEventMap
: T extends Document
? DocumentEventMap
: T extends Window
? WindowEventMap
: HTMLElementEventMap;
type EventTypes<T extends EventTarget> = keyof EventMap<T> & string;
type EventValue<T extends EventTarget, K extends EventTypes<T>> = Extract<EventMap<T>[K], Event>;
function listen<T extends EventTarget, K extends EventTypes<T>>(
element: T,
type: K,
listener: (this: T, ev: EventValue<T, K>) => void,
opts: AddEventListenerOptions
) {
element.addEventListener(type, listener, opts);
return element.removeEventListener.bind(element, type, listener, opts);
} |
@pushkine works perfectly for me, thanks! why aren't these in the standard types already? 🙂 |
@pushkine I've tried your workaround unsuccessfully in TS Playground: AFAICT, |
Prefix may be added back later on but for right now it is not a necessary Other small changes include - Changing j to J and let to const - better typing of event listener options with AddEventListenerOptions - Adds EventMap type from microsoft/TypeScript#33047 (comment) - Updates JuhlaMethodsFunc & JuhlaEmitFunc with EventMap generic - Removes JuhlaEventListenerOptions
Search Terms
EventListener, EventMap, EventTarget
Suggestion
There should a type to retrieve valid event names and listeners for a given
EventTarget
.Window
has the following method:Every
EventTarget
should have its own correspondingEventMap
(even if no special event types are available for a given target, thus, resulting in an empty object). TheEventMap
of a givenEventTarget
could be retrieved as follows:Use Cases
I tried creating a function which encapsulates an event for easier lifecycle management with React Hooks:
Unfortunately,
EventListener
gives no proper IntelliSense and I had to use theas EventListener
syntax like below, as suggested in #28357:My goal was to simplify the syntax to the following, with proper type inference:
Using conditional types, I was able to achieve the syntax above by replacing
EventListener
with a specializedEventListenerCallback
type which extracts values from the 2 most commonly used event maps, namelyWindowEventMap
andDocumentEventMap
:The new code for
managedEventListener
was born:Examples
The code above could be greatly simplified by introducing the aforementioned
EventMap<EventTarget>
type:Checklist
My suggestion meets these guidelines:
The text was updated successfully, but these errors were encountered: