TypeDrop
2026-09-23 Challenge
2026-09-23
Easy
Typed Recipe Ingredient Scaler
You're building the core logic for a recipe management app. Home cooks can scale any recipe up or down by a multiplier, and the app must return a fully-typed scaled recipe where every ingredient's quantity has been adjusted and units are preserved exactly as the compiler understands them.
Goals
- Define the `Unit` union type and `Ingredient` / `Recipe` interfaces.
- Derive `ScaledIngredient` and `ScaledRecipe` exclusively with utility types (Omit, Pick, intersection) — no hand-written interfaces.
- Implement `scaleRecipe` so quantities are rounded to 2 decimal places and servings to the nearest integer.
- Implement the generic `findIngredientsByUnit<U extends Unit>` returning `readonly (Ingredient & { unit: U })[]`.
challenge.ts
// Key types you must define and the two function signatures:
type Unit = "tsp" | "tbsp" | "cup" | "oz" | "lb"
| "g" | "kg" | "ml" | "l"
| "whole" | "pinch";
interface Ingredient { name: string; quantity: number; unit: Unit }
interface Recipe { title: string; servings: number; ingredients: readonly Ingredient[] }
// Derived with utility types (no hand-written interfaces!):
type ScaledIngredient = /* Omit/Pick/& combo */ never;
type ScaledRecipe = /* Omit/& combo */ never;
function scaleRecipe(recipe: Recipe, multiplier: number): ScaledRecipe { ... }
function findIngredientsByUnit<U extends Unit>(
recipe: Recipe,
unit: U
): readonly (Ingredient & { unit: U })[] { ... }
Hints (click to reveal)
Hints
- For `ScaledIngredient`, try `Omit<Ingredient, 'quantity'> & { quantity: number }` — then ask yourself whether that's meaningfully different from just `Ingredient`, and whether a `Pick`-based approach communicates intent more clearly.
- For `ScaledRecipe`, `Omit<Recipe, 'servings' | 'ingredients'> & { servings: number; ingredients: readonly ScaledIngredient[] }` is the right shape — no new keywords needed.
- Inside `findIngredientsByUnit`, a simple `.filter()` with a type predicate `(i): i is Ingredient & { unit: U } => i.unit === unit` will satisfy the compiler without any casting.
Useful resources
Or clone locally
git clone -b challenge/2026-09-23 https://github.com/niltonheck/typedrop.git