type StringEventKey = Extract; type AnyListener = (...args: any[]) => void; type EventArgs = TEvents[TEventName] extends readonly [...infer A] ? [...A] : TEvents[TEventName] extends readonly any[] ? [...TEvents[TEventName]] : [TEvents[TEventName]]; export default class TypedEventEmitter> { private listeners = new Map, Set>(); emit>( eventName: TEventName, ...eventArg: EventArgs ) { const handlers = this.listeners.get(eventName); if (!handlers || handlers.size === 0) { return; } // Copy handlers to avoid issues if listeners mutate during emit [...handlers].forEach((handler) => { handler(...eventArg); }); } on>( eventName: TEventName, handler: (...eventArg: EventArgs) => void, ) { const handlers = this.listeners.get(eventName) ?? new Set(); handlers.add(handler as AnyListener); this.listeners.set(eventName, handlers); } removeListener>( eventName: TEventName, handler: (...eventArg: EventArgs) => void, ) { const handlers = this.listeners.get(eventName); if (!handlers) { return; } handlers.delete(handler as AnyListener); if (handlers.size === 0) { this.listeners.delete(eventName); } } }