TypeDrop
A new TypeScript challenge every day. Sharpen your types.
TypeDrop delivers a fresh TypeScript challenge every day, generated by AI. Pick a challenge, open it in StackBlitz (preferred) or CodeSandbox (or clone it locally), and make the tests pass. No accounts, no setup — just you and the type system.
2026-04-07
Hard
Typed Query Plan Optimizer
You're building the query execution layer for an in-browser analytics engine. Raw query descriptors arrive as unknown JSON; your optimizer must validate them, build a typed expression tree, walk it with a recursive visitor, and return a fully typed execution plan with cost estimates — with zero `any`.
Goals
- Define the `Expr` and `QueryNode` discriminated union types and the generic `ExprVisitor<R>` interface.
- Implement `walkExpr` with an exhaustive switch so TypeScript catches any missing branch at compile time.
- Implement `parseExpr` and `parseQueryNode` to safely validate unknown blobs into fully typed trees, returning `Result<T, ParseError>`.
- Implement `estimateRows`, `buildPlan`, and the top-level `optimize` function, propagating errors and accumulating large-table warnings correctly.
challenge.ts
// Core types and main entry point
export type Expr =
| { kind: "literal"; value: string | number | boolean | null }
| { kind: "column"; table: string; name: string }
| { kind: "binary"; op: BinaryOp; left: Expr; right: Expr }
| { kind: "agg"; fn: AggFn; source: ColumnRef }
| { kind: "case"; whens: ReadonlyArray<{ when: Expr; then: Expr }>; else: Expr };
export type QueryNode =
| { op: "scan"; table: string; alias: string }
| { op: "filter"; predicate: Expr; child: QueryNode }
| { op: "project"; outputs: ReadonlyArray<{ alias: string; expr: Expr }>; child: QueryNode }
| { op: "join"; condition: Expr; left: QueryNode; right: QueryNode }
| { op: "agg"; groupBy: ReadonlyArray<ColumnRef>; aggregates: ReadonlyArray<{ alias: string; expr: AggExpr }>; child: QueryNode };
export interface ExprVisitor<R> {
visitLiteral(e: LiteralExpr): R;
visitColumn (e: ColumnRef): R;
visitBinary (e: BinaryExpr): R;
visitAgg (e: AggExpr): R;
visitCase (e: CaseExpr): R;
}
// Main entry point — parse, plan, and cost an unknown query descriptor
export function optimize(raw: unknown): Result<ExecutionPlan, OptimizerError>;
Hints (click to reveal)
Hints
- Use a `switch (expr.kind)` with a `default: satisfies never` (or an exhaustive helper) to guarantee every Expr variant is handled in `walkExpr`.
- In `parseExpr` and `parseQueryNode`, narrow `unknown` step-by-step: check `typeof`, then `'kind' in raw`, then check specific string values before casting to the target type.
- The `totalCost` in `optimize` is the sum of `estimatedRows` across every node in the `PlanNode` tree — write a small private recursive helper that mirrors the structure of `PlanNode`.
Useful resources
Or clone locally
git clone -b challenge/2026-04-07 https://github.com/niltonheck/typedrop.git