TypeDrop
2026-04-13 Challenge
2026-04-13
Hard
Typed Workflow Orchestrator
You're building the execution engine for a low-code automation platform. Raw workflow definitions arrive as unknown JSON blobs from a database; your orchestrator must validate them, compile them into a strongly-typed execution graph, run steps through a typed middleware pipeline with retry logic, and surface a discriminated-union result per step — with zero `any`.
Goals
- Define `HandlerRegistry` as a mapped type over `WorkflowStep["kind"]` using `Extract` to bind each key to the correct `StepHandler` generic instantiation.
- Implement `parseWorkflowDef` to validate unknown input into a fully-typed `WorkflowDef`, checking all structural invariants and returning the first `ValidationError` encountered.
- Implement `composeMiddleware` to chain an array of `MiddlewareFn` wrappers around a terminal handler, preserving outermost-first execution order.
- Implement `runWorkflow` to parse, dispatch, retry, branch on conditions, collect `StepOutcome` records, and return a typed `WorkflowReport` — handling every discriminated step kind exhaustively.
challenge.ts
// Core types and main orchestrator signature
type Result<T, E> =
| { readonly ok: true; value: T }
| { readonly ok: false; error: E };
type WorkflowStep = HttpStep | TransformStep | ConditionStep | NotifyStep;
// TODO 2: Define this as a mapped type
type HandlerRegistry = /* mapped over WorkflowStep["kind"] */ unknown;
type MiddlewareFn = (
step: WorkflowStep,
next: () => Promise<Result<unknown, ExecutionError>>
) => Promise<Result<unknown, ExecutionError>>;
// Main entry point — parse, compose, execute, report
declare function runWorkflow(
raw: unknown,
registry: HandlerRegistry,
middlewares?: readonly MiddlewareFn[]
): Promise<Result<WorkflowReport, OrchestratorError>>;
Hints (click to reveal)
Hints
- For `HandlerRegistry`, try: `{ [K in WorkflowStep["kind"]]: StepHandler<Extract<WorkflowStep, { kind: K }>> }` — the mapped type does the narrowing for you.
- In `composeMiddleware`, build the chain from the inside out using `reduceRight` so that the first middleware in the array becomes the outermost wrapper.
- In `withRetry`, thread the attempt index through a recursive async helper rather than a `for` loop — it makes the exponential back-off calculation cleaner and avoids mutable state.
Useful resources
Or clone locally
git clone -b challenge/2026-04-13 https://github.com/niltonheck/typedrop.git