TypeDrop

2026-04-15 Challenge

2026-04-15 Easy

Typed CSV Report Parser

You're building the data-import pipeline for a sales analytics dashboard. Raw CSV text arrives from uploaded files as plain strings; your parser must validate each row, transform it into a strongly-typed record, and return a typed parse report — with zero `any`.

Goals

  • Define the `RowResult` discriminated union and complete the `ParseReport` interface, including using `Extract<>` for the `failures` field.
  • Implement the generic `ColumnSchema<T>` interface and fill in all six `SALE_COLUMNS` entries with correct parse and validate logic, using `satisfies`.
  • Implement `parseRow` to validate cell counts, catch parse errors, collect validation errors, and return the correct `RowResult` variant.
  • Implement `parseCSV` (header validation, blank-line skipping, report assembly) and `revenueByRegion` (per-region sum via `Record<Region, number>`, zero-initialised, rounded to 2 dp).
challenge.ts
// Core domain types + main function signature

export type Region = "NA" | "EU" | "APAC" | "LATAM";

export interface SaleRecord {
  id: string;
  region: Region;
  product: string;
  quantity: number;
  unitPrice: number;
  saleDate: Date;
}

// Generic column descriptor — T defaults to string
export interface ColumnSchema<T = string> {
  header: string;
  parse: (raw: string) => T;        // may throw on bad input
  validate: (value: T) => string | null; // null = valid
}

// Discriminated union for per-row parse outcome
export type RowResult =
  | { ok: true;  value: SaleRecord }
  | { ok: false; rowIndex: number; raw: string; errors: string[] };

// Main entry point
export function parseCSV(csv: string): ParseReport { /* TODO */ }
Hints (click to reveal)

Hints

  • The `failures` field type in `ParseReport` should use `Extract<RowResult, { ok: false }>[]` — let TypeScript derive the narrowed type rather than duplicating it.
  • For `SALE_COLUMNS`, each entry has its own concrete `T`; annotate the array with `satisfies ReadonlyArray<ColumnSchema<unknown>>` so TypeScript checks each entry without widening individual `parse` return types.
  • Inside `parseRow`, iterate over `schemas` with an index; wrap each `schema.parse(cells[i])` in a try/catch and only call `schema.validate` if parsing succeeded — then decide `ok: true` or `ok: false` at the end.

Or clone locally

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