type StringKeyOf = Extract; type CallbackType, EVENT extends StringKeyOf> = T[EVENT] extends any[] ? T[EVENT] : [T[EVENT]]; type CallbackFunction, EVENT extends StringKeyOf> = (...props: CallbackType) => any export default class EventEmitter> { private callbacks: { [key: string]: Function[] } = {} public on>(event: EVENT, fn: CallbackFunction): this { if (!this.callbacks[event]) { this.callbacks[event] = [] } this.callbacks[event].push(fn) return this } protected emit>(event: EVENT, ...args: CallbackType): this { const callbacks = this.callbacks[event] if (callbacks) { callbacks.forEach(callback => callback.apply(this, args)) } return this } public off>(event: EVENT, fn?: CallbackFunction): this { const callbacks = this.callbacks[event] if (callbacks) { if (fn) { this.callbacks[event] = callbacks.filter(callback => callback !== fn) } else { delete this.callbacks[event] } } return this } protected removeAllListeners(): void { this.callbacks = {} } }