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.

Or clone locally

git clone -b challenge/2026-04-04 https://github.com/niltonheck/typedrop.git