TypeDrop
2026-09-25 Challenge
2026-09-25
Medium
Typed CSV Pipeline with Schema Validation & Aggregation
You're building the data-ingestion core for a financial reporting tool. Raw CSV text arrives from uploaded files, each column is validated against a declared schema, and the parsed rows are aggregated into a strongly-typed summary report — all without a single `any` or unsafe cast.
Goals
- Complete the `SchemaRow<S>` mapped type so it produces a correct intersection of per-column object types using `UnionToIntersection`.
- Implement `parseCsv` to split raw CSV text, validate headers, parse each cell to its declared kind, handle optional nulls, and collect errors without `any`.
- Implement `aggregateRows` to group rows by a string-valued key and sum specified numeric columns, returning a fully-typed `GroupSums` object.
- Implement `runPipeline` to compose parsing and aggregation into a single typed call, returning an empty report when no rows are valid.
challenge.ts
// Key types at a glance:
type ColumnKind = "string" | "number" | "boolean" | "date";
type KindToPrimitive<K extends ColumnKind> =
K extends "string" ? string :
K extends "number" ? number :
K extends "boolean" ? boolean :
K extends "date" ? Date : never;
interface ColumnDef<K extends ColumnKind = ColumnKind> {
readonly name: string;
readonly kind: K;
readonly optional?: boolean;
}
// SchemaRow maps a tuple of ColumnDefs → one intersected object type
type SchemaRow<S extends readonly ColumnDef[]> = UnionToIntersection<...>;
// Main entry point:
function parseCsv<S extends readonly ColumnDef[]>(
rawCsv: string,
schema: S
): CsvParseOutput<S>;
function aggregateRows<
S extends readonly ColumnDef[],
Spec extends AggregationSpec<S>
>(rows: SchemaRow<S>[], spec: Spec): GroupSums<S, Spec>;
Hints (click to reveal)
Hints
- For `SchemaRow<S>`, first map the tuple to a union of single-key objects, then wrap that union in `UnionToIntersection<...>` to collapse it into one flat object.
- In `parseCsv`, a `switch` on `col.kind` is the cleanest way to narrow to the right branch — the compiler will know the return type of each arm without any casting.
- For `NumericKeys<S>` and the `groupBy` constraint in `AggregationSpec`, use a mapped type that maps each key to itself or `never`, then index with `[keyof SchemaRow<S>]` to filter.
Useful resources
Or clone locally
git clone -b challenge/2026-09-25 https://github.com/niltonheck/typedrop.git