TypeDrop

2026-02-26 Challenge

2026-02-26 Easy

Typed Product Inventory Aggregator

You're building a back-office tool for an e-commerce warehouse. Raw inventory records arrive as unknown JSON, and you must safely validate them, group them by category, and compute per-category summaries — the challenge is keeping every step fully typed without reaching for `any`.

Goals

  • Implement `parseProduct` to safely narrow `unknown` → `Product` using a `Result` discriminated union — no `any` or `as` allowed.
  • Implement `groupByCategory` to bucket valid products into a `Map<Category, Product[]>`.
  • Implement `summariseCategory` to compute per-category totals, averages (2 d.p.), and the cheapest product using `Pick<Product, ...>`.
  • Implement `buildInventoryReport` to orchestrate the full pipeline: parse all raw records, collect indexed `ParseError`s, aggregate summaries, and return a sorted `InventoryReport`.
challenge.ts

// Key types
export type Category = "electronics" | "clothing" | "food" | "furniture";

export interface Product {
  id: string;
  name: string;
  category: Category;
  priceUsd: number;   // > 0
  stock: number;      // non-negative integer
}

export type Result<T, E> =
  | { ok: true;  value: T }
  | { ok: false; error: E };

export interface CategorySummary {
  category: Category;
  productCount: number;
  totalStock: number;
  averagePriceUsd: number;
  cheapestProduct: Pick<Product, "id" | "name" | "priceUsd">;
}

// Functions to implement
export function parseProduct(raw: unknown): Result<Product, string> { ... }
export function groupByCategory(products: Product[]): Map<Category, Product[]> { ... }
export function summariseCategory(category: Category, products: Product[]): CategorySummary { ... }
export function buildInventoryReport(rawRecords: unknown[]): InventoryReport { ... }
Hints (click to reveal)

Hints

  • Narrow `unknown` incrementally: first check `typeof raw === 'object' && raw !== null`, then use the `in` operator to confirm each field exists before checking its type or value.
  • The `Result<T, E>` union is discriminated on the `ok` boolean — TypeScript will narrow `value` vs `error` automatically inside an `if (result.ok)` block.
  • For `Pick<Product, 'id' | 'name' | 'priceUsd'>`, you don't need to construct a new type — just return an object literal with exactly those three keys and TypeScript will enforce the shape.

Or clone locally

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