TypeDrop
2026-08-08 Challenge
2026-08-08
Easy
Typed Event Emitter with Discriminated Payloads
You're building the notification layer for a real-time collaboration tool. Components fire strongly-typed domain events (user joined, document edited, cursor moved) and listeners must only receive the exact payload shape for the event they subscribed to — no casting, no `any`.
Goals
- Define an `EventMap` interface that binds three event name literals to their distinct payload shapes.
- Implement the `Listener<E, K>` generic type alias so callbacks are constrained to exactly `E[K]`.
- Build the `TypedEmitter<E>` class with `on`, `off`, `emit`, and `once` methods that preserve full payload types.
- Export a `createRoomEmitter` factory with an explicit `TypedEmitter<EventMap>` return type annotation.
challenge.ts
// Key types you'll define and implement:
export interface EventMap {
"user:joined": { userId: string; roomId: string; timestamp: number };
"doc:edited": { docId: string; delta: string; authorId: string };
"cursor:moved": { userId: string; x: number; y: number };
}
export type Listener<E extends EventMap, K extends keyof E> = /* TODO */ never;
export class TypedEmitter<E extends EventMap> {
on<K extends keyof E>(event: K, listener: Listener<E, K>): void { /* TODO */ }
off<K extends keyof E>(event: K, listener: Listener<E, K>): void { /* TODO */ }
emit<K extends keyof E>(event: K, payload: E[K]): void { /* TODO */ }
once<K extends keyof E>(event: K, listener: Listener<E, K>): void { /* TODO */ }
}
export function createRoomEmitter(): TypedEmitter<EventMap> { /* TODO */ }
Hints (click to reveal)
Hints
- An indexed access type like `E[K]` is the key to connecting an event name to its payload — use it in both the `Listener` alias and the `emit` signature.
- For internal storage, a `Map<keyof E, Array<Listener<E, keyof E>>>` works, but you may need a small cast-free trick: store each bucket as `Set<Function>` keyed by the event string — think about why `Map<K, Set<Listener<E,K>>>` is tricky with a single Map and how to work around it.
- `once` can be implemented entirely in terms of `on` and `off` — wrap the user's listener in a new closure that calls `off` on itself before invoking the original.
Useful resources
Or clone locally
git clone -b challenge/2026-08-08 https://github.com/niltonheck/typedrop.git