TypeDrop

2026-07-18 Challenge

2026-07-18 Easy

Typed In-Memory Event Emitter

You're building the notification hub for a real-time dashboard. UI components subscribe to named events and expect their callbacks to receive exactly the right payload shape — no more, no less. Your task is to implement a strongly-typed `EventEmitter` that maps event names to their payload types and enforces that relationship at every call site.

Goals

  • Implement a generic `EventEmitter<EventMap>` class where `EventMap` maps event name strings to payload types.
  • Ensure `on`, `off`, and `once` enforce that listeners receive exactly the payload type declared in `EventMap[K]`.
  • Implement `once` so the listener auto-unsubscribes after its first invocation.
  • Return `this` from `on`, `off`, and `once` to enable fluent method chaining — without using `any` or type assertions.
challenge.ts

// Core event map shape — maps event names → payload types
interface DashboardEventMap {
  userLoggedIn:    { userId: string; timestamp: number };
  metricUpdated:   { metricName: string; value: number };
  alertTriggered:  { severity: "low" | "medium" | "high"; message: string };
  connectionClosed:{ code: number };
}

// Your job: implement this class
class EventEmitter<EventMap extends Record<string, unknown>> {
  on<K extends keyof EventMap>(
    event: K,
    listener: (payload: EventMap[K]) => void
  ): this { ... }

  off<K extends keyof EventMap>(
    event: K,
    listener: (payload: EventMap[K]) => void
  ): this { ... }

  emit<K extends keyof EventMap>(event: K, payload: EventMap[K]): boolean { ... }

  once<K extends keyof EventMap>(
    event: K,
    listener: (payload: EventMap[K]) => void
  ): this { ... }

  listenerCount<K extends keyof EventMap>(event: K): number { ... }
}
Hints (click to reveal)

Hints

  • The key to type safety is the double-generic pattern: `<K extends keyof EventMap>` on each method lets TypeScript infer the exact event name and look up `EventMap[K]` for the payload.
  • For `once`, wrap the user's listener in an inner function that calls `off` on itself before invoking the original — store the wrapper, not the original, in your listener map.
  • A `Map<keyof EventMap, Set<...>>` works well for storage, but you'll need to think carefully about what type the `Set` holds to avoid `any`.

Or clone locally

git clone -b challenge/2026-07-18 https://github.com/niltonheck/typedrop.git