TypeDrop

2026-05-26 Challenge

2026-05-26 Medium

Typed Event Aggregator with Discriminated Union Streams

You're building the analytics ingestion layer for a real-time monitoring dashboard. Raw telemetry events arrive as `unknown` from a WebSocket feed; your engine must validate them, fan them into typed streams by category, and produce a strongly-typed per-category summary report — with zero `any`.

Goals

  • Define `TypedEvent` as a three-member discriminated union and derive `EventCategory`, `EventByCategory<C>`, and `SummaryByCategory<C>` from it using conditional types or `Extract<>` — no hardcoded strings in utility types.
  • Implement `validateRawEvent` and `parseEvent` to narrow `unknown` inputs into fully typed events, returning a `ParseResult` discriminated union without any `as` assertions.
  • Implement the generic `extractCategory<C>` so that TypeScript statically narrows the returned array element type to the correct `TypedEvent` member for each category.
  • Implement the generic `summarize<C>` to compute `ErrorSummary`, `MetricSummary`, or `AuditSummary` with correct aggregation logic (topCode, per-name averages, unique actors), with the return type statically resolving to the right summary via `SummaryByCategory<C>`.
challenge.ts
// Core discriminated union & key types you must define:

export type TypedEvent =
  | { category: "error";  id: string; ts: number; payload: ErrorPayload  }
  | { category: "metric"; id: string; ts: number; payload: MetricPayload }
  | { category: "audit";  id: string; ts: number; payload: AuditPayload  };

export type EventCategory = TypedEvent["category"]; // derive, don't hardcode

export type EventByCategory<C extends EventCategory> = /* your conditional type */;
export type SummaryByCategory<C extends EventCategory> = /* your conditional type */;

// Main generic functions you must implement:
export function extractCategory<C extends EventCategory>(
  results: ParseResult[],
  category: C
): EventByCategory<C>[];

export function summarize<C extends EventCategory>(
  results: ParseResult[],
  category: C
): SummaryByCategory<C>;
Hints (click to reveal)

Hints

  • For `EventByCategory<C>`, try `Extract<TypedEvent, { category: C }>` — it resolves to exactly the union member whose `category` matches `C`.
  • For `SummaryByCategory<C>`, a conditional type chain `C extends 'error' ? ErrorSummary : C extends 'metric' ? MetricSummary : AuditSummary` is the cleanest approach — but you'll still need a type assertion-free way to return it from `summarize`; consider a type predicate or exhaustive if/else that TypeScript can follow.
  • In `validateRawEvent`, use `typeof`, `Array.isArray`, and `Object.keys` checks — never `as`. Once all checks pass, TypeScript will accept the return type.

Or clone locally

git clone -b challenge/2026-05-26 https://github.com/niltonheck/typedrop.git