Skip to content

Instantly share code, notes, and snippets.

@w8r
Created July 23, 2025 12:20
Show Gist options
  • Save w8r/7067dc5e851c792b4eb905bda923d179 to your computer and use it in GitHub Desktop.
Save w8r/7067dc5e851c792b4eb905bda923d179 to your computer and use it in GitHub Desktop.
Typed Event Emitter
type EventMap = Record<string, any>;
export class TypedEventEmitter<Events extends EventMap> extends EventTarget {
on<K extends keyof Events>(type: K, listener: (event: Events[K]) => void): void {
const wrapper = (e: Event) => listener((e as CustomEvent).detail);
// @ts-ignore store wrapper so .off() can remove it later
listener.__wrapper__ = wrapper;
this.addEventListener(type as string, wrapper as EventListener);
}
off<K extends keyof Events>(type: K, listener: (event: Events[K]) => void): void {
// @ts-ignore remove stored wrapper
this.removeEventListener(type as string, listener.__wrapper__ as EventListener);
}
once<K extends keyof Events>(type: K, listener: (event: Events[K]) => void): void {
const wrapper = (e: Event) => {
listener((e as CustomEvent).detail);
this.removeEventListener(type as string, wrapper);
};
this.addEventListener(type as string, wrapper as EventListener);
}
emit<K extends keyof Events>(type: K, detail: Events[K]): void {
this.dispatchEvent(new CustomEvent(type as string, { detail }));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment