HardPro challengePythonJavaScriptTypeScript
TypedEmitter<EventMap>
TypeScriptTypesPatterns
A standard EventEmitter accepts string events and any payloads. A TypedEmitter uses a generic EventMap to enforce correct types per event:
type AppEvents = {
'user:login': { userId: string; at: Date };
'challenge:solved': { challengeId: string; score: number };
'error': Error;
};
const emitter = new TypedEmitter<AppEvents>();
// ✅ TypeScript enforces the correct payload type:
emitter.on('user:login', ({ userId, at }) => { ... });
emitter.emit('user:login', { userId: 'u_1', at: new Date() });
// ❌ TypeScript errors:
emitter.on('unknown-event', () => {}); // not in AppEvents
emitter.emit('user:login', { message: 'hello' }); // wrong payload shapeThe type key: K extends keyof TMap
on<K extends keyof TMap>(event: K, handler: (payload: TMap[K]) => void): this
// ↑ binds K to a valid event name
// ↑ payload type is derived from the event nameImplement
| Method | Behavior |
|---|---|
on(event, handler) | Subscribe (chainable) |
off(event, handler) | Unsubscribe (chainable) |
emit(event, payload) | Call all handlers |
once(event, handler) | Subscribe, auto-unsubscribe after first call |
Sample tests
Test #1on then emit — handler receives payload
Input: [[["on","click","h1"],["emit","click",{"x":1,"y":2}]]]
Output: [{"event":"click","payload":{"x":1,"y":2}}]
Test #2emit fires handler each time
Input: [[["on","data","h1"],["emit","data",42],["emit","data",99]]]
Output: [{"event":"data","payload":42},{"event":"data","payload":99}]
Test #3off unsubscribes the handler
Input: [[["on","msg","h1"],["off","msg","h1"],["emit","msg","hello"]]]
Output: []
Test #4once only fires on first emit
Input: [[["once","ping","h1"],["emit","ping",1],["emit","ping",2]]]
Output: [{"event":"ping","payload":1}]
Test #5emit with no handlers is a no-op
Input: [[["emit","no-listeners","anything"]]]
Output: []