TypeDrop
2026-04-20 Challenge
2026-04-20
Medium
Typed Event Stream Aggregator
You're building the real-time analytics backend for a product telemetry platform. Raw event objects arrive as `unknown` from a WebSocket feed; your aggregator must validate them, route them to per-event-type handlers, and produce a strongly-typed session summary — with zero `any`.
Goals
- Define a discriminated union `TelemetryEvent` with 5 variants sharing `sessionId` and `timestamp` fields.
- Implement `parseTelemetryEvent` as a type-guard that narrows `unknown` to `TelemetryEvent | null` using narrowing only — no `as`.
- Build an `EventHandlerMap` mapped type and `createHandlerMap` factory that routes each event kind to a handler mutating a `SessionSummary`.
- Implement `aggregateSession` that parses, filters by session ID, routes events, and returns a fully-typed `AggregationReport`.
challenge.ts
// Key types and main function signature preview
export type EventKind =
| "page_view" | "button_click" | "error" | "purchase" | "session_end";
// Discriminated union — one member shown as example:
// { kind: "purchase"; sessionId: string; timestamp: number;
// orderId: string; amountCents: number; currency: string }
export type TelemetryEvent = /* your discriminated union */ never;
export type SessionSummary = {
sessionId : string;
pageViews : string[];
clicks : Record<string, number>;
errors : Array<{ code: number; message: string; fatal: boolean }>;
totalSpentCents: number;
currencies : Set<string>;
endedAt : number | null;
};
export type EventHandlerMap =
{ [K in EventKind]: (event: Extract<TelemetryEvent, { kind: K }>) => void };
export function parseTelemetryEvent(raw: unknown): TelemetryEvent | null { /* TODO */ return null; }
export function aggregateSession(
sessionId : string,
rawEvents : unknown[],
): AggregationReport { /* TODO */ return null!; }
Hints (click to reveal)
Hints
- A mapped type `{ [K in EventKind]: (e: Extract<TelemetryEvent, { kind: K }>) => void }` lets TypeScript verify every kind has a handler — and gives each handler a narrowed event type for free.
- In `parseTelemetryEvent`, check `typeof raw === 'object' && raw !== null` first, then use `'url' in raw` style checks for each variant — the compiler will narrow the type at each step.
- Track `kindCounts` as `Record<EventKind, number>` initialised with all five keys set to `0`; updating it inside `aggregateSession` (not inside the handlers) keeps the report logic clean.
Useful resources
Or clone locally
git clone -b challenge/2026-04-20 https://github.com/niltonheck/typedrop.git