TypeDrop

2026-06-01 Challenge

2026-06-01 Hard

Typed Real-Time Event Stream Aggregator

You're building the ingestion layer for a live operations monitoring platform. Raw telemetry events arrive as `unknown` over a simulated stream; your engine must validate them into a discriminated-union event type, route them through per-kind typed reducer functions, enforce a sliding time-window deduplication strategy, and produce a strongly-typed per-source aggregation report — with zero `any`.

Goals

  • Define branded scalar types and a discriminated-union TelemetryEvent with per-kind payload shapes.
  • Implement a runtime validator that narrows unknown → TelemetryEvent | null without any casts until all checks pass.
  • Build a generic AccumulatorMap mapped type and three pure typed reducer functions dispatched exhaustively by event kind.
  • Implement a sliding-window deduplicator closure and wire everything together in aggregateStream to produce a fully-typed AggregationReport.
challenge.ts
// Key types & main function signature

type Brand<Base, Tag extends string> = Base & { readonly __brand: Tag };

type EventId   = Brand<string, "EventId">;
type SourceId  = Brand<string, "SourceId">;
type Timestamp = Brand<number, "Timestamp">;

type TelemetryEvent =
  | { kind: "metric"; id: EventId; sourceId: SourceId; timestamp: Timestamp; payload: MetricPayload }
  | { kind: "log";    id: EventId; sourceId: SourceId; timestamp: Timestamp; payload: LogPayload   }
  | { kind: "alert";  id: EventId; sourceId: SourceId; timestamp: Timestamp; payload: AlertPayload };

type AccumulatorMap = {
  metric: MetricAccumulator;
  log:    LogAccumulator;
  alert:  AlertAccumulator;
};

interface AggregationReport {
  sources:        Map<SourceId, SourceReport>;
  totalIngested:  number;
  totalDropped:   number;
}

function aggregateStream(
  rawEvents: unknown[],
  dedupConfig: DeduplicationConfig
): AggregationReport { /* TODO */ }
Hints (click to reveal)

Hints

  • For AccumulatorMap, a simple interface-based mapped type `{ [K in TelemetryEvent["kind"]]: ... }` plus a conditional type avoids any index-signature tricks.
  • In aggregateStream, write a small `applyReducer(report: SourceReport, event: TelemetryEvent): void` helper that switches on `event.kind` — TypeScript will narrow both the event and the accumulator type inside each branch without needing casts.
  • The `satisfies` operator in parseEvent lets you build the return object as a plain literal and have the compiler verify the discriminated-union shape in one shot, rather than asserting types piecemeal.

Or clone locally

git clone -b challenge/2026-06-01 https://github.com/niltonheck/typedrop.git