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.

Or clone locally

git clone -b challenge/2026-04-20 https://github.com/niltonheck/typedrop.git