typestar

A typed event emitter in TypeScript

A map of event names to payloads makes on and emit type-safe.

type EventMap = {
  run: { lang: string; tpm: number };
  cleared: { set: string };
  error: Error;
};

class Emitter<Events extends Record<string, unknown>> {
  #handlers = new Map<keyof Events, Set<(payload: never) => void>>();

  on<K extends keyof Events>(type: K, handler: (payload: Events[K]) => void) {
    const set = this.#handlers.get(type) ?? new Set();
    set.add(handler as (payload: never) => void);
    this.#handlers.set(type, set);
    return () => set.delete(handler as (payload: never) => void);
  }

  emit<K extends keyof Events>(type: K, payload: Events[K]): number {
    const set = this.#handlers.get(type);
    set?.forEach((handler) => (handler as (p: Events[K]) => void)(payload));
    return set?.size ?? 0;
  }
}

const bus = new Emitter<EventMap>();
bus.on("run", ({ tpm }) => console.log("tpm", tpm));
console.log(bus.emit("run", { lang: "ts", tpm: 98 }));
// bus.emit("run", { lang: "ts" }); // error: tpm is missing

How it works

  1. The event map is the single source of truth.
  2. K extends keyof Events ties the payload to the name.
  3. A wrong payload, or an unknown event, fails to compile.

Keywords and builtins used here

The run, in numbers

Lines
27
Characters to type
905
Tokens
280
Three-star pace
110 tpm

At the three-star pace of 110 tokens a minute, this run takes about 153 seconds.

Type this snippet

Step 1 of 2 in Typed events, step 3 of 8 in Async & modules.

← Previous Next →