TypeDrop
2026-06-05 Challenge
2026-06-05
Easy
Typed Recipe Ingredient Scaler
You're building the recipe engine for a meal-planning app. Raw recipe data arrives as `unknown` from a third-party nutrition API; your engine must validate it, scale each ingredient's quantity to a requested serving size, and return a strongly-typed scaled recipe — with zero `any`.
Goals
- Implement `isUnit()` as a strict type guard using the `UNITS` constant.
- Implement `parseIngredient()` and `parseRecipe()` to validate `unknown` input into typed domain objects, returning discriminated `ValidationError` values.
- Implement `scaleRecipe()` to proportionally adjust ingredient quantities and return a `ScaledRecipe` with `originalServings` and `targetServings` fields.
- Implement `parseAndScale()` to compose the validation and scaling steps, tagging pipeline errors with a `stage` discriminant.
challenge.ts
// Key types & main function signature
export type Unit = "g" | "kg" | "ml" | "l" | "tsp" | "tbsp" | "cup" | "piece" | "pinch";
export interface Ingredient { name: string; quantity: number; unit: Unit; }
export interface Recipe { id: string; title: string; servings: number; ingredients: Ingredient[]; }
export interface ScaledRecipe extends Recipe { originalServings: number; targetServings: number; }
export type Result<T, E extends string = string> =
| { ok: true; value: T }
| { ok: false; error: E };
export type PipelineError =
| { stage: "validation"; error: ValidationError }
| { stage: "scaling"; error: ScalingError };
// Full pipeline: validate raw unknown → scale to targetServings
export function parseAndScale(
raw: unknown,
targetServings: number
): Result<ScaledRecipe, PipelineError> { /* TODO */ }
Hints (click to reveal)
Hints
- Use `Array.prototype.includes` with a cast-free approach on `UNITS` to implement `isUnit` — remember `UNITS` is `readonly Unit[]`, so `(UNITS as readonly unknown[]).includes(value)` keeps you type-safe.
- In `parseRecipe`, check `typeof raw === 'object' && raw !== null` before accessing any fields, then use `'fieldName' in raw` to confirm keys exist before reading them.
- For `parseAndScale`, unwrap each `Result` step-by-step and wrap errors with the correct `stage` tag before returning — no casting needed.
Useful resources
Or clone locally
git clone -b challenge/2026-06-05 https://github.com/niltonheck/typedrop.git