type Action = (...args: Args) => void; interface EventTypes { [name: string]: readonly any[]; } interface EventCache { [name: string]: Set>; } export class EventEmitter { private _cache: EventCache = {}; public emit(name: E, args: T[E]): void { if (!this._cache[name as string]) { return; } this._cache[name as string].forEach((callback) => { callback(...args); }); } public on(name: E, callback: Action): void { if (!this._cache[name as string]) { this._cache[name as string] = new Set>(); } this._cache[name as string].add(callback); } public off(name: E, callback: Action): void { if (!this._cache[name as string]) { return; } this._cache[name as string].add(callback); } } const t = new EventEmitter<{ test: [someValue: string] }>(); t.emit('test', ['a']); const handler = (): void => {}; t.on('test', handler); t.off('test', handler);