DS
Back to TypeScript

observer.ts

typescript/patterns/observer.ts
TypeScript

Observer / EventEmitter pattern with typed events and automatic cleanup.

observer.ts
/**
 * @description Observer / EventEmitter pattern with typed events and automatic cleanup.
 * @tags pattern, events, observer, pub-sub
 */
type Listener<T> = (event: T) => void;
type UnsubscribeFn = () => void;

export class EventBus<Events extends Record<string, unknown>> {
  private listeners = new Map<keyof Events, Set<Listener<unknown>>>();

  on<K extends keyof Events>(event: K, listener: Listener<Events[K]>): UnsubscribeFn {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, new Set());
    }
    const set = this.listeners.get(event)!;
    set.add(listener as Listener<unknown>);

    return () => {
      set.delete(listener as Listener<unknown>);
      if (set.size === 0) this.listeners.delete(event);
    };
  }

  emit<K extends keyof Events>(event: K, data: Events[K]): void {
    const set = this.listeners.get(event);
    if (!set) return;
    for (const listener of set) {
      listener(data);
    }
  }

  clear(): void {
    this.listeners.clear();
  }
}