CodeBlanks Logo Icon CodeBlanks

State Encoding

Learn state machines with phantom types.

State encoding

Phantom types can be used to encode the stages of a state machine into the type. Each state is represented by its own phantom argument; transition functions accept only values in the correct state. This way invalid transitions become compile errors.

type Order<S> = { amount: number };

type Pending = Order<"pending">;
type Paid = Order<"paid">;
type Shipped = Order<"shipped">;

function pay(o: Pending): Paid { return o; }
function ship(o: Paid): Shipped { return o; }

Transition functions

Transition functions separate input and output types with phantom arguments; for example, pay takes Order<"pending"> and returns Order<"paid">. This ensures the function can only be called in the correct order.

const p: Pending = { amount: 10 };
const paid = pay(p);        // Pending -> Paid
const shipped = ship(paid); // Paid -> Shipped

const bad = ship(p);  // error: Pending is not Paid

Practice Order flow with state encoding

Define the Order<S> generic type (body amount: number). Create Pending, Paid, Shipped types with phantom arguments. Write pay(o: Pending): Paid and ship(o: Paid): Shipped functions. Start from a Pending value, apply pay and ship, and print the amount of the result via console.log.

Starter code

index.ts
// Order<S> generic türünü tanımla (S gövdede kullanılmasın)

// Pending, Paid, Shipped türlerini phantom argümanlarla oluştur

// pay ve ship geçiş fonksiyonlarını yaz

// Pending değerinden başla, pay ve ship uygula, sonucu yazdır

Sample solution

index.ts
type Order<S> = { amount: number };

type Pending = Order<"pending">;
type Paid = Order<"paid">;
type Shipped = Order<"shipped">;

function pay(o: Pending): Paid {
  return o;
}

function ship(o: Paid): Shipped {
  return o;
}

const p: Pending = { amount: 10 };
const shipped = ship(pay(p));

console.log(shipped.amount);

Expected output

10

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 What is the core advantage of state encoding with phantoms?

  • Performing state checks at runtime
  • Faster code execution
  • Lower memory usage
  • Catching invalid state transitions at compile time Correct answer

Explanation: With phantoms each state is a distinct type, so out-of-order transitions (e.g. shipping a Pending order) become compile errors; no runtime check is needed.

2 Transition function signature

Question
type Order<S> = { amount: number };
type Pending = Order<"pending">;
type Paid = Order<"paid">;

function pay(o: Pending): ____ { return o; }
Answer
type Order<S> = { amount: number };
type Pending = Order<"pending">;
type Paid = Order<"paid">;

function pay(o: Pending): Paid { return o; }

Explanation: The pay function takes Pending and returns Paid; this enforces the state transition at the type level and prevents out-of-order calls.

3 State encoding parts

Term Meaning
Pending durumu unpaid order
Paid durumu paid order
pay fonksiyonu takes Pending returns Paid
ship(p) çağrısı compile error (wrong state)

All lessons in Advanced