TypeDrop

2026-03-08 Challenge

2026-03-08 Easy

Typed Inventory Aggregator

You're building the stock-management module for an e-commerce back-office. Raw inventory records arrive from multiple warehouses and must be validated, grouped by category, and summarised — all with zero `any` and fully typed results.

Goals

  • Implement `validateRecord` to narrow a `RawRecord` into an `InventoryItem` or return a typed `ValidationError` using a `Result` discriminated union.
  • Implement `partitionRecords` to collect all valid items and all errors in a single pass without short-circuiting.
  • Implement `aggregateByCategory` to group items and compute per-category statistics (`totalItems`, `totalQuantity`, `totalValue`, `averageUnitPrice`) in a single loop.
  • Compose the full pipeline in `processInventory`, returning both the aggregated summary and any validation errors.
challenge.ts

// Key types & main signatures at a glance

export type Category = "electronics" | "clothing" | "food" | "furniture";

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

export interface InventoryItem extends Omit<RawRecord, "category"> {
  category: Category;           // narrowed from raw string
}

export interface CategorySummary {
  category: Category;
  totalItems: number;
  totalQuantity: number;
  totalValue: number;           // sum of quantity * unitPrice (cents)
  averageUnitPrice: number;     // rounded mean unitPrice across SKUs
}

// ── Functions you must implement ──────────────────────────────────────────────
export function validateRecord(raw: RawRecord): Result<InventoryItem, ValidationError>;
export function partitionRecords(raws: RawRecord[]): { valid: InventoryItem[]; errors: ValidationError[] };
export function aggregateByCategory(items: InventoryItem[]): Partial<Record<Category, CategorySummary>>;
export function processInventory(raws: RawRecord[]): { summary: Partial<Record<Category, CategorySummary>>; errors: ValidationError[] };
Hints (click to reveal)

Hints

  • A `Set` or tuple literal like `["electronics", "clothing", "food", "furniture"] as const` makes it easy to check whether a raw string is a valid `Category` — and TypeScript will narrow the type for you after the check.
  • For `aggregateByCategory`, use `Array.prototype.reduce` with a `Partial<Record<Category, CategorySummary>>` accumulator so you can build each entry on first encounter and update it on subsequent ones — all in one pass.
  • The `Result<T, E>` discriminated union lets you branch on `.ok` without any type assertions — lean on that in `partitionRecords` to route each outcome to the right bucket.

Or clone locally

git clone -b challenge/2026-03-08 https://github.com/niltonheck/typedrop.git