TypeDrop

2026-06-30 Challenge

2026-06-30 Easy

Typed Shopping Cart with Discount Resolution

You're building the checkout module for a small e-commerce storefront. Raw cart payloads arrive as `unknown` from the client; your module must validate them into strongly-typed `CartItem` records, apply the correct discount strategy per item category, and return a fully-typed order summary — with zero `any`.

Goals

  • Define `Category`, `CartItem`, `Discount` (discriminated union), and `DiscountPolicy` (mapped/Record type) so that missing or misspelled categories cause compile errors.
  • Implement `parseCartItem` to safely narrow `unknown` → `CartItem`, throwing a descriptive `TypeError` on any invalid field.
  • Implement `applyDiscount` with an exhaustive `switch` on `discount.kind`, using `assertNever` in the default branch so new variants can never be silently ignored.
  • Implement `buildOrderSummary` to collect ALL parse errors before returning, and produce a correctly rounded `grandTotalUsd` on the happy path.
challenge.ts
// Key types & main entry point

type Category = "electronics" | "clothing" | "food" | "books";

interface CartItem {
  id: string;
  name: string;
  category: Category;
  priceUsd: number;
  quantity: number;
}

type Discount =
  | { kind: "percentage"; percent: number }
  | { kind: "fixed"; amountUsd: number }
  | { kind: "none" };

type DiscountPolicy = Record<Category, Discount>;

type OrderSummary =
  | { ok: true;  lines: OrderLine[]; grandTotalUsd: number }
  | { ok: false; errors: string[] };

function buildOrderSummary(
  rawItems: unknown[],
  policy: DiscountPolicy
): OrderSummary { /* TODO */ }
Hints (click to reveal)

Hints

  • A `Record<Category, Discount>` mapped type forces every category key to be present — try omitting one and watch TypeScript complain.
  • In `parseCartItem`, a helper like `const VALID_CATEGORIES: readonly Category[] = [...]` combined with `.includes()` (with a type predicate) is a clean way to validate the category field without `any`.
  • The `assertNever` helper is already provided in the stub — make sure your `switch` covers all three `Discount` variants so the `default` branch is actually unreachable.

Or clone locally

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