TypeDrop
2026-04-23 Challenge
2026-04-23
Easy
Typed Shopping Cart Aggregator
You're building the order summary engine for a small e-commerce storefront. Raw cart line items arrive as `unknown` JSON from the client; your engine must validate them, apply typed discount rules, and produce a strongly-typed order summary — with zero `any`.
Goals
- Define a generic `Result<T, E>` discriminated union and use it as the return type for all three functions.
- Implement `parseLineItem` to validate an `unknown` value into a `LineItem` with a computed subtotal, returning typed errors on failure.
- Implement `validateDiscount` to validate an `unknown` value into a `DiscountRule`, branching correctly on the `kind` discriminant.
- Implement `buildOrderSummary` to validate all inputs, apply ordered discount rules, and produce a fully-typed `OrderSummary`.
challenge.ts
// Key types you'll work with:
type Category = "electronics" | "clothing" | "food" | "books";
type DiscountRule =
| { kind: "percentage"; percent: number; category?: Category }
| { kind: "fixed"; amountOff: number; minimumSubtotal: number };
type Result<T, E> = /* TODO */ never;
type CartValidationError =
| { kind: "empty_cart" }
| { kind: "invalid_item"; index: number; reason: string }
| { kind: "invalid_discount"; index: number; reason: string };
// Main entry point:
function buildOrderSummary(
rawItems: unknown[],
rawDiscounts: unknown[]
): Result<OrderSummary, CartValidationError>
Hints (click to reveal)
Hints
- Start with `Result<T, E>` — it's a two-member discriminated union on an `ok` boolean field; all three functions return it.
- To check if a value belongs to a union like `Category`, declare a `const VALID_CATEGORIES` tuple and use `Array.prototype.includes` with a type predicate.
- In `buildOrderSummary`, compute per-category subtotals with a `Record<Category, number>` accumulator before finding `topCategory`.
Useful resources
Or clone locally
git clone -b challenge/2026-04-23 https://github.com/niltonheck/typedrop.git