TypeDrop

2026-10-25 Challenge

2026-10-25 Easy

Typed Recipe Cost Calculator

You're building the pricing engine for a meal-kit delivery app. Recipes are made up of ingredients with quantities and units, and you need to calculate costs, flag expensive recipes, and summarize a weekly menu — with the compiler enforcing every unit, field name, and summary shape.

Goals

  • Implement `toBaseUnit` using a `Record<Unit, number>` multiplier map to convert any quantity to its ingredient's base unit.
  • Implement `calculateRecipeCost` to look up prices, convert units, sum ingredient costs, and populate a fully-typed `RecipeCost` object.
  • Implement `summarizeMenu` to aggregate an array of `RecipeCost` values into a `MenuSummary` — including `Pick`-typed cheapest/most-expensive fields — and throw a `RangeError` on empty input.
  • Implement `filterByMaxCostPerServing` to return a sorted, filtered `readonly RecipeCost[]` within a given per-serving budget.
challenge.ts
/** All supported units of measure */
type Unit = "g" | "kg" | "ml" | "l" | "unit";

interface IngredientPrice {
  readonly name: string;
  readonly pricePerBaseUnit: number;
  readonly baseUnit: Extract<Unit, "g" | "ml" | "unit">;
}

interface RecipeCost {
  readonly recipeId: string;
  readonly recipeName: string;
  readonly totalCost: number;
  readonly costPerServing: number;
  readonly isExpensive: boolean;
}

interface MenuSummary {
  readonly totalRecipes: number;
  readonly averageCostPerServing: number;
  readonly cheapestRecipe: Pick<RecipeCost, "recipeId" | "recipeName" | "costPerServing">;
  readonly mostExpensiveRecipe: Pick<RecipeCost, "recipeId" | "recipeName" | "costPerServing">;
  readonly expensiveCount: number;
}

// Convert any Unit quantity to its base unit (g, ml, or unit)
function toBaseUnit(quantity: number, unit: Unit): number { ... }

// Calculate full cost breakdown for one recipe
function calculateRecipeCost(
  recipe: Recipe,
  prices: readonly IngredientPrice[],
  expensiveThreshold: number
): RecipeCost { ... }

// Aggregate an array of RecipeCost into a MenuSummary
function summarizeMenu(costs: readonly RecipeCost[]): MenuSummary { ... }
Hints (click to reveal)

Hints

  • A `Record<Unit, number>` multiplier (e.g. `{ kg: 1000, l: 1000, g: 1, ml: 1, unit: 1 }`) makes `toBaseUnit` a one-liner — no `if`/`switch` needed.
  • Remember that `Array.find` returns `T | undefined`; use optional chaining (`?.`) or a nullish fallback to avoid a type error when an ingredient isn't in the price list.
  • `Pick<RecipeCost, 'recipeId' | 'recipeName' | 'costPerServing'>` is already the declared type in `MenuSummary` — you only need to return an object with exactly those three keys, which TypeScript will verify structurally.

Or clone locally

git clone -b challenge/2026-10-25 https://github.com/niltonheck/typedrop.git