TypeDrop
2026-05-29 Challenge
2026-05-29
Easy
Typed Shopping Cart Aggregator
You're building the order-summary layer for an e-commerce checkout flow. Raw cart items arrive as `unknown` from a localStorage deserializer; your engine must validate them, apply typed discount rules, and produce a strongly-typed order summary — with zero `any`.
Goals
- Implement `validateCartItem` to narrow `unknown` → `ValidatedCartItem` using a branded type, throwing a `TypeError` on any invalid field.
- Implement `buildLineItems` to map validated items into `LineItem` objects with computed line totals.
- Implement `applyDiscounts` to accumulate discounts from all three `DiscountRule` kinds (flat, percentage, bogo) against the original subtotal, clamped to [0, subtotal].
- Compose everything in `buildOrderSummary` to produce a fully-typed `OrderSummary` with a non-negative total.
challenge.ts
// Key types at a glance
type Category = "electronics" | "clothing" | "food" | "books";
type ValidatedCartItem = RawCartItem & { readonly __validated: true };
type DiscountRule =
| { kind: "flat"; amountInCents: number }
| { kind: "percentage"; percent: number }
| { kind: "bogo"; category: Category };
interface OrderSummary {
lineItems: LineItem[];
subtotalInCents: number;
discountInCents: number;
totalInCents: number; // never negative
itemCount: number;
}
// Functions you must implement:
declare function validateCartItem(raw: unknown): ValidatedCartItem;
declare function buildLineItems(items: ReadonlyArray<ValidatedCartItem>): LineItem[];
declare function applyDiscounts(
subtotalInCents: number,
items: ReadonlyArray<ValidatedCartItem>,
rules: ReadonlyArray<DiscountRule>
): number;
declare function buildOrderSummary(
items: ReadonlyArray<ValidatedCartItem>,
rules: ReadonlyArray<DiscountRule>
): OrderSummary;
Hints (click to reveal)
Hints
- Use a type predicate (`value is ValidatedCartItem`) or an explicit brand assignment inside `validateCartItem` — remember, no `as` allowed means you must narrow field-by-field before returning.
- For the discriminated union in `applyDiscounts`, a `switch` on `rule.kind` gives you exhaustive narrowing and TypeScript will enforce that every branch is handled.
- The `bogo` discount only needs the validated items array — filter by `rule.category`, then sum each matching item's `lineTotalInCents` and halve it with `Math.floor`.
Useful resources
Or clone locally
git clone -b challenge/2026-05-29 https://github.com/niltonheck/typedrop.git