TypeScript

Type-Safe Event Emitter

admin by @admin ADMIN
19h ago
May 31, 2026
Public
0 0 up · 0 down Sign in to vote
A 30-line type-safe mini EventEmitter. The event-name → payload map is enforced at compile time, so emit and on calls can't drift apart.
TypeScript
Raw
type Listener<T> = (payload: T) => void;

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

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

  off<K extends keyof Events>(event: K, fn: Listener<Events[K]>): void {
    this.listeners[event] = this.listeners[event]?.filter(l => l !== fn);
  }

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

type AppEvents = {
  'user:login':  { id: number; name: string };
  'user:logout': { id: number };
};

const bus = new TypedEmitter<AppEvents>();
bus.on('user:login', u => console.log(`hi ${u.name}`));      // u: { id; name }
bus.emit('user:login', { id: 1, name: 'Alice' });            // ✓
// bus.emit('user:login', { id: 1 });                         // ✗ missing 'name'
Tags

Save your own code snippets

Create a free account and build your private vault. Share publicly whenever you want.