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`.

Or clone locally

git clone -b challenge/2026-04-23 https://github.com/niltonheck/typedrop.git