TypeDrop
2026-06-17 Challenge
2026-06-17
Easy
Typed Shopping Cart Price Calculator
You're building the checkout feature for an e-commerce storefront. Raw cart data arrives as `unknown` from a client-side POST request; your engine must validate it, apply typed discount rules, and return a strongly-typed order summary — with zero `any`.
Goals
- Implement `validateCart` to narrow `unknown` → `Cart` using type guards, collecting every `ValidationError` before returning.
- Implement `buildOrderSummary` to compute per-line totals, subtotal, and a correctly capped `discountApplied` amount using discriminated-union narrowing on `DiscountKind`.
- Implement `processCart` to compose the two helpers and return a `Result<OrderSummary, ValidationError[]>`.
- Use zero `any` and zero type assertions (`as`) throughout — rely solely on type guards and control flow narrowing.
challenge.ts
export type Currency = "USD" | "EUR" | "GBP";
export type DiscountKind =
| { kind: "percentage"; percent: number }
| { kind: "fixed"; amount: number };
export interface Cart {
currency: Currency;
items: CartItem[];
discount?: DiscountKind;
}
export type Result<T, E> =
| { ok: true; value: T }
| { ok: false; error: E };
export type ValidationError =
| { field: "currency"; message: string }
| { field: "items"; message: string }
| { field: `items[${number}]`; message: string }
| { field: "discount"; message: string };
/** Validate raw unknown input → typed Cart, collecting ALL errors. */
export function validateCart(raw: unknown): Result<Cart, ValidationError[]>;
/** Compute line totals, subtotal, discount, and final total. */
export function buildOrderSummary(cart: Cart): OrderSummary;
/** Validate + calculate in one call. */
export function processCart(raw: unknown): Result<OrderSummary, ValidationError[]>;
Hints (click to reveal)
Hints
- A `const CURRENCIES = ['USD','EUR','GBP'] as const` array lets you write a clean type-guard without repeating the union members.
- Narrow `DiscountKind` in `buildOrderSummary` with a `switch (cart.discount.kind)` — TypeScript will fully narrow each branch for you.
- For the template-literal error field `items[${number}]`, just interpolate the index: `` `items[${i}]` as const `` satisfies the union member exactly.
Useful resources
Or clone locally
git clone -b challenge/2026-06-17 https://github.com/niltonheck/typedrop.git