import { EventEmitter } from "events"; type TEventName = string | symbol; type AnyListener = (...args: any[]) => void; type Arguments = TListener extends (...args: infer TArgs) => any ? TArgs : any[]; type Listener = TEvents[TEvent] extends ( ...args: infer TArgs ) => any ? (...args: TArgs) => void : AnyListener; export interface DefaultEvents { newListener: (event: TEventName, listener: AnyListener) => void; removeListener: (event: TEventName, listener: AnyListener) => void; } export type AnyEvents = DefaultEvents & { [event in TEventName]: any[] }; type IEventSubscriber = < TEvent extends keyof TEvents & TEventName >( event: TEvent, listener: Listener ) => This; // tslint:disable:ban-types interface ITypedEventEmitter { on: IEventSubscriber; off: IEventSubscriber; once: IEventSubscriber; addListener: IEventSubscriber; removeListener: IEventSubscriber; prependListener: IEventSubscriber; prependOnceListener: IEventSubscriber; emit( event: TEvent, ...args: Arguments ): boolean; listeners( event: TEvent ): Function[]; rawListeners( event: TEvent ): Function[]; eventNames(): Array; setMaxListeners(maxListeners: number): this; getMaxListeners(): number; listenerCount( event: TEvent ): number; } const TypedEventEmitter = EventEmitter as { new (): TypedEventEmitter; }; type TypedEventEmitter< TEvents extends DefaultEvents = AnyEvents > = ITypedEventEmitter; type Constructable = new (...args: any[]) => any; export function typedEventEmitter< TBase extends Constructable, TEvents extends DefaultEvents = AnyEvents >(Base: TBase): TBase & TypedEventEmitter { const NewClass = class extends Base { constructor(...args: any[]) { super(...args); EventEmitter.call(this as any); } }; Object.getOwnPropertyNames(EventEmitter.prototype).forEach(name => { NewClass.prototype[name] = (EventEmitter.prototype as any)[name]; }); return NewClass as any; } export { TypedEventEmitter };