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.
Useful resources
Or clone locally
git clone -b challenge/2026-06-30 https://github.com/niltonheck/typedrop.git