TypeDrop
2026-09-02 Challenge
2026-09-02
Easy
Typed Shopping Cart with Discount Strategy & Line-Item Aggregation
You're building the checkout engine for a small e-commerce storefront. Each cart holds typed line items, and the store supports several mutually-exclusive discount strategies (percentage off, fixed amount off, buy-X-get-Y free). The compiler must guarantee that every discount kind is fully handled and that the cart summary is always correctly typed.
Goals
- Derive `DiscountKind` from the `Discount` union using an indexed access type, and build `DiscountByKind` as a mapped type using `Extract`.
- Implement `computeSubtotal` to aggregate line-item costs in a single pass.
- Implement `computeDiscountAmount` with an exhaustive `switch` on `discount.kind`, correctly handling all three strategies including the buy-X-get-Y free calculation.
- Implement `buildCartSummary` and `getDiscountLabel` so the compiler guarantees every discount branch is covered and all return types are fully typed.
challenge.ts
export interface LineItem {
readonly product: Product;
readonly quantity: number;
}
export type Discount =
| { kind: "percentage"; percent: number }
| { kind: "fixed"; amountCents: number }
| { kind: "buyXgetY"; productId: string; buyQuantity: number; freeQuantity: number };
export type DiscountKind = Discount["kind"];
export type DiscountByKind = {
[K in DiscountKind]: Extract<Discount, { kind: K }>;
};
export function computeSubtotal(lineItems: readonly LineItem[]): number;
export function computeDiscountAmount(
subtotalCents: number,
discount: Discount,
lineItems: readonly LineItem[]
): number;
export function buildCartSummary(
lineItems: readonly LineItem[],
discount: Discount | null
): CartSummary;
Hints (click to reveal)
Hints
- For `DiscountByKind`, iterate over `DiscountKind` with a mapped type and use `Extract<Discount, { kind: K }>` as the value — TypeScript will narrow each member automatically.
- In `computeDiscountAmount`, a `switch (discount.kind)` with a `default: { const _exhaustive: never = discount; }` guard ensures the compiler catches any unhandled future discount kinds.
- For buy-X-get-Y, the number of free units from `n` total units is `Math.floor(n / (buyQuantity + freeQuantity)) * freeQuantity`.
Useful resources
Or clone locally
git clone -b challenge/2026-09-02 https://github.com/niltonheck/typedrop.git