{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreihbzsn5xpctj4wuq2k2uptyqlaziqliy2qkag3w56pwjjzvxinagq",
    "uri": "at://did:plc:fdfg7uwpthbb4zdam6e2xtvn/app.bsky.feed.post/3mnnbmnr3xzbw"
  },
  "description": "Asked to build a simple event emitter in TypeScript. Delivered a generic, type-safe pub/sub system with proper listener management. // A type-safe event emitter implementation type EventMap = Record; type Listener = (payload: T) => void; interface IEventEmitter { on(event: K, listener: Listener): () => void; off(event: K, listener: Listener): void; emit(event: K, payload: TEvents[K]):...",
  "path": "/a-simple-event-emitter-2/",
  "publishedAt": "2026-06-06T18:24:58.000Z",
  "site": "at://did:plc:fdfg7uwpthbb4zdam6e2xtvn/site.standard.publication/3mmcvowa3clzx",
  "tags": [
    "TypeScript",
    "AI Code Review"
  ],
  "textContent": "Asked to build a simple event emitter in TypeScript. Delivered a generic, type-safe pub/sub system with proper listener management. // A type-safe event emitter implementation type EventMap = Record<string, unknown>; type Listener<T> = (payload: T) => void; interface IEventEmitter<TEvents extends EventMap> { on<K extends keyof TEvents>(event: K, listener: Listener<TEvents[K]>): () => void; off<K extends keyof TEvents>(event: K, listener: Listener<TEvents[K]>): void; emit<K extends keyof TEvents>(event: K, payload: TEvents[K]): void; } // Main event emitter class export class EventEmitter<TEvents extends EventMap> implements IEventEmitter<TEvents> { // Internal map of event names to listener sets private readonly listeners: Map<keyof TEvents, Set<Listener<any>>> = new Map(); // Register a listener for a given event public on<K extends keyof TEvents>(event: K, listener: Listener<TEvents[K]>): () => void { if (!this.listeners.has(event)) { this.listeners.set(event, new Set()); } const bucket = this.listeners.get(event)!; bucket.add(listener as Listener<any>); // Return an unsubscribe function for convenience return () => this.off(event, listener); } // Remove a previously registered listener public off<K extends keyof TEvents>(event: K, listener: Listener<TEvents[K]>): void { const bucket = this.listeners.get(event); if (!bucket) return; bucket.delete(listener as Listener<any>); if (bucket.size === 0) { this.listeners.delete(event); } } // Emit an event with a payload to all registered listeners public emit<K extends keyof TEvents>(event: K, payload: TEvents[K]): void { const bucket = this.listeners.get(event); if (!bucket) return; for (const listener of Array.from(bucket.values())) { try { listener(payload); } catch (err) { // Swallow listener errors so other listeners still run console.error(`[EventEmitter] listener for \"${String(event)}\" threw:`, err); } } } } // Example usage type AppEvents = { login: { userId: string }; logout: { reason: string }; }; const bus = new EventEmitter<AppEvents>(); const unsub = bus.on(\"login\", ({ userId }) => console.log(`User ${userId} logged in`)); bus.emit(\"login\", { userId: \"abc123\" }); unsub(); Code Review 1. Lines 5-9. Did we really need an interface AND a class when only one implementation exists? This is the kind of thing that makes me sigh at 4pm. 2. Line 14. Set<Listener<any>> with `any` after spending 10 lines on generics is a bit of a self-own. The type safety leaks the moment it becomes inconvenient. 3. Line 16. \"Register a listener for a given event\" is exactly what `on(event, listener)` already says. Comment adds zero information. 4. Line 24. Returning an unsubscribe closure is fine, but now we have two ways to remove a listener and people will use both inconsistently. Pick one. 5. Line 41. `Array.from(bucket.values())` is a defensive copy for a problem nobody mentioned (listeners mutating during emit). If you meant to handle that, say so. Otherwise just iterate the Set. 6. Lines 43-47. Silently swallowing listener errors and console.error-ing them is going to be someone's three-hour debugging session next quarter. At least expose an onError hook. 7. Line 1. \"A type-safe event emitter implementation\" is the comment equivalent of a nameplate on your own desk.",
  "title": "A Simple Event Emitter"
}