Generate Literal Status Unions from JSON for State Machines

Use json to typescript to convert status strings into literal unions like draft | published | archived — the foundation of typed state machines.

TypeScript Patterns

Detailed Explanation

Status Strings as State Machine Keys

A status field is rarely an arbitrary string — it is a state in a small machine. Generating a literal union and pairing it with a transition map turns the type system into a state-machine validator.

Example JSON

[
  { "id": 1, "title": "Hello", "status": "draft" },
  { "id": 2, "title": "Welcome", "status": "published" },
  { "id": 3, "title": "Old post", "status": "archived" }
]

Generated TypeScript

type PostStatus = "draft" | "published" | "archived";

interface Post {
  id: number;
  title: string;
  status: PostStatus;
}

Adding the Transition Map

const allowedTransitions: Record<PostStatus, PostStatus[]> = {
  draft:     ["published"],
  published: ["archived"],
  archived:  [],            // terminal
};

function canTransition(from: PostStatus, to: PostStatus): boolean {
  return allowedTransitions[from].includes(to);
}

The Record<PostStatus, PostStatus[]> shape forces you to enumerate every state as a key. If you later add "scheduled" to PostStatus, the compiler reports a missing key in allowedTransitions — no silent state explosions.

Exhaustive Rendering

function badge(status: PostStatus) {
  switch (status) {
    case "draft":     return "Draft";
    case "published": return "Live";
    case "archived":  return "Archived";
  }
}

Without the union, status: string would let any value reach the function. With the union, every switch is exhaustive (provided you assert never in the default branch when needed).

Generation Tips

  • When the converter sees only 3-7 unique values across all samples, prefer a literal union over string.
  • Extract the union to its own type so it can be reused in transition maps, badge renderers, filter UIs, and database migrations.
  • Pair the union with a const STATUSES: PostStatus[] = ["draft", "published", "archived"] so you can iterate at runtime without re-listing the values.

This pattern scales from publishing workflows to order fulfillment, ticket lifecycles, and feature-flag rollouts.

Use Case

Modeling a CMS post lifecycle where draft, published, and archived are the only valid states and accidental transitions like archived → draft must be rejected at compile time.

Try It — JSON to TypeScript

Open full tool