TypeScript

Типизированный EventEmitter

Algonexys · 07.08.2026 · 👁 0

Строгая типизация имён событий и их полезной нагрузки — ошибки ловятся при компиляции.

Код

type Listener<T> = (payload: T) => void;

export class EventEmitter<Events extends Record<string, unknown>> {
  private listeners: { [K in keyof Events]?: Set<Listener<Events[K]>> } = {};

  on<K extends keyof Events>(event: K, listener: Listener<Events[K]>): () => void {
    (this.listeners[event] ??= new Set()).add(listener);
    return () => this.off(event, listener);
  }

  once<K extends keyof Events>(event: K, listener: Listener<Events[K]>): void {
    const off = this.on(event, (payload) => {
      off();
      listener(payload);
    });
  }

  off<K extends keyof Events>(event: K, listener: Listener<Events[K]>): void {
    this.listeners[event]?.delete(listener);
  }

  emit<K extends keyof Events>(event: K, payload: Events[K]): void {
    this.listeners[event]?.forEach((listener) => listener(payload));
  }
}

interface AppEvents {
  login: { userId: number; at: Date };
  message: { text: string };
}

const bus = new EventEmitter<AppEvents>();
bus.on('login', ({ userId }) => console.log('Вход:', userId));
bus.emit('login', { userId: 1, at: new Date() });
// bus.emit('login', { userId: '1' });  ← ошибка типа на этапе компиляции