TypeDrop
2026-04-04 Challenge
2026-04-04
Hard
Typed Workflow State Machine
You're building the order-fulfillment engine for a logistics platform. Every order moves through a strict lifecycle — and your state machine must enforce legal transitions at the type level, accumulate a typed audit log, and return exhaustively-matched `Result<T, E>` outcomes, all with zero `any`.
Goals
- Define `OrderState` as a discriminated union where each variant's payload is derived from `StatePayloadMap` and the `kind` field is the literal `StateName` key.
- Brand `Transition<From, To>` with phantom type parameters so that transitions between incompatible states are rejected at compile time, and register all legal transitions using `satisfies`.
- Implement `applyTransition` to validate the target state, check the TRANSITIONS registry, verify all required payload fields are present, and return a typed `Result<MachineContext, TransitionError>`.
- Implement `runWorkflow` and `summariseMachine` — the former short-circuits on the first error, the latter produces a `WorkflowSummary` using a single `reduce` pass with no intermediate arrays.
challenge.ts
// Key types and main function signatures
export type StateName =
| "pending" | "confirmed" | "picking"
| "packed" | "shipped" | "delivered" | "cancelled";
// TODO 1 — build this from StatePayloadMap
export type OrderState =
| { kind: "pending"; orderId: string; placedAt: string }
| { kind: "confirmed"; orderId: string; confirmedAt: string; warehouseId: string }
| /* ... all 7 variants */ never;
// TODO 2 — phantom-branded transition
export type Transition<From extends StateName, To extends StateName> = never;
// TODO 3 — registry validated with `satisfies`
export const TRANSITIONS = { /* "pending->confirmed": ..., ... */ } as const;
export function applyTransition(
ctx: MachineContext,
input: TransitionInput
): Result<MachineContext, TransitionError>;
export function runWorkflow(
initialPayload: { orderId: string; placedAt: string },
transitions: readonly TransitionInput[]
): Result<MachineContext, TransitionError>;
export function summariseMachine(ctx: MachineContext): WorkflowSummary;
Hints (click to reveal)
Hints
- For the `OrderState` union, try using a mapped type over `StatePayloadMap` and then `[StateName][number]` to distribute it into a flat union.
- A phantom type is just a structural type that includes a field (or intersection) whose type encodes `From` and `To` but is never actually present at runtime — look at how `{ readonly _brand: readonly [From, To] }` can be intersected.
- In `applyTransition`, build a `REQUIRED_FIELDS` lookup (keyed by `StateName`) to know which payload keys to check — you can derive it from `StatePayloadMap` at the type level and mirror it as a runtime constant.
Useful resources
Or clone locally
git clone -b challenge/2026-04-04 https://github.com/niltonheck/typedrop.git