type EventMap = Record<string, any[]>
type EventListener<T extends EventMap, K extends keyof T> = (...args: T[K]) => void | Promise<void>
type TypedEventMap<T extends EventMap> = { [K in keyof T]?: Array<EventListener<T, K>> }
type SuperEventListener<P extends CustomEventEmitter, T extends EventMap, K extends keyof T> = (instance: P, ...args: T[K]) => void | Promise<void>
type TypedSuperEventMap<P extends CustomEventEmitter, T extends EventMap> = { [K in keyof T]?: Array<SuperEventListener<P, T, K>> }
export class CustomEventEmitter<T extends EventMap = EventMap> {
constructor(..._: any[]) {}
protected _events: TypedEventMap<T> = {};
protected get event_constructor() {
return this.constructor.name;
}
protected get _super_events() {
return (CustomEventEmitter.__events[this.event_constructor] ||= {}) as TypedSuperEventMap<CustomEventEmitter<any>, T>;
}
on<K extends keyof T>(event: K, listener: EventListener<T, K>): this {
(this._events[event] ||= []).push(listener);
return this;
}
off<K extends keyof T>(event: K, listener: EventListener<T, K>): this {
const index = this._events[event]?.indexOf(listener);
if(typeof index === "number" && index > -1) this._events[event]!.splice(index, 1);
return this;
}
async emit<K extends keyof T>(event: K, ...args: T[K]): Promise<boolean> {
const event_listeners = (this._events[event] || []);
const super_event_listeners = this._super_events[event] || [];
for(const listener of event_listeners) {
await listener(...args);
}
for(const listener of super_event_listeners) {
await listener(this, ...args as T[K]);
}
return true;
}
protected static __events: Record<string, TypedEventMap<EventMap>> = {};
private static get _events() {
return this.__events[this.name] ||= {};
}
static on<
InstanceType extends CustomEventEmitter,
eventName extends keyof InstanceType['__event_map']
>(
this: Constructor<InstanceType> & typeof CustomEventEmitter<any>,
event: eventName,
listener: (instance: InstanceType, ...args: InstanceType['__event_map'][eventName] ) => any
) {
(this._events[event.toString()] ||= []).push(listener);
return this;
}
static off<
InstanceType extends CustomEventEmitter,
eventName extends keyof InstanceType['__event_map']
>(
this: Constructor<InstanceType> & typeof CustomEventEmitter<any>,
event: eventName,
listener: (instance: InstanceType, ...args: InstanceType['__event_map'][eventName]) => any
) {
const index = this._events[event.toString()]?.indexOf(listener as any);
if(typeof index === "number" && index > -1) this._events[event.toString()]!.splice(index, 1);
return this;
}
declare __event_map: T
}
type Constructor<T> = new (...args: any[]) => T;