import { EventEmitter, Listener } from "events" import { StringKeys } from "@/lib/util/types" type BaseEventConfig = { [key: string]: any } type EventArgs< E extends keyof EventConfig, EventConfig extends BaseEventConfig > = EventConfig[E] extends undefined ? [] : [EventConfig[E]] interface _TypedEventEmitter { emit>( event: E, ...args: EventArgs ): boolean on>( event: E, listener: (...args: EventArgs) => void ): _TypedEventEmitter off>( event: E, listener: (...args: EventArgs) => void ): _TypedEventEmitter once>( event: E, listener: (...args: EventArgs) => void ): _TypedEventEmitter prependListener>( event: E, listener: (...args: EventArgs) => void ): _TypedEventEmitter prependOnceListener>( event: E, listener: (...args: EventArgs) => void ): _TypedEventEmitter eventNames(): StringKeys[] listenerCount>(event: E): number removeAllListeners>( event?: E ): _TypedEventEmitter rawListeners>(event: E): Listener[] listeners>(event: E): Listener[] addListener: _TypedEventEmitter["on"] removeListener: _TypedEventEmitter["off"] } type TypedEventEmitterInstance = _TypedEventEmitter & Omit> /** * A standard EventEmitter that provides enhanced typing for specific events. * * The EventConfig generic type should map event names to the value * that will be passed to a listener. The event emitter is then required to * emit events with the correct value type, and listeners must expect * the correct type parameter. * * @example * ```typescript * interface MyEvents { * customEventA: string, * customEventB: { foo: 'bar' | 'baz' }, * customEventC: undefined, * } * * const emitter = new TypedEventEmitter() * * emitter.emit('customEventA', 'something') // verified payload for customEventA * emitter.emit('customEventB', { foo: 'bar' }) // verified payload for customEventB * emitter.emit('customEventC') // arg not required for customEventC when payload is undefined * * emitter.on('customEventA', (s: string) => console.log(s)) // verified listener arg for customEventA * emitter.on('customEventB', (obj) => console.log(obj.foo)) // verified listener arg for customEventB * emitter.on('customEventC', () => console.log('C fired')) // no listener arg for customEventC when payload is undefined * ``` */ export const TypedEventEmitter = EventEmitter as any as new < EventConfig extends BaseEventConfig >() => TypedEventEmitterInstance