Checkpoint: Request State

The Course
•
4 min read

A type can represent a request identity, but only a runtime comparison can decide whether a completion belongs to the current request. A reducer is a function taking the current state and an action, then returning the next state. It does not start requests or change its input object. Implement one for idle, loading and ready states. Start creates loading with an id; finish is accepted only while loading with that same id.

Request ids are generated by the caller and must be unique within its lifecycle. Starting request 2 after request 1 makes a later finish for 1 stale. Ignore it by returning the exact unchanged state object. This makes a no-op observable and avoids unnecessary updates.

The TypeScript state union prevents reading an id from idle without narrowing. It does not prove ids are unique or increasing. Keep those assumptions documented and test both matching and stale transitions.

Predict, run, and explain

Write the output and trace the important state changes before checking it. Then run the program. Use Check types first; execution is allowed only when the real compiler accepts the source.

Predict, then runTypeScript
type State =
  | { tag: "idle" }
  | {
      tag: "loading";

      request: number;
    };

const state: State = { tag: "loading", request: 2 };

console.log(state.request === 1);
console.log(state.request === 2);

Enable JavaScript to edit and type-check this example.

Check your prediction
false
true

Acceptance cases before you code

Write a small test plan before implementing request-state ownership. These cases extend the brief; they are not a second worked solution. Predict the expected behavior independently, then test through the public function. A good ordinary example and a good boundary example should catch different possible mistakes.

Case to tryWhat your result should demonstrate
Finish old request while newer is loadingReturn the exact newer state unchanged
Finish matching current requestConstruct ready with its title
Finish while idle or already readyFollow the documented no-op transition rule

After the core task passes, deliberately break one rule and identify the case that notices it. Restore the implementation and explain the failure in terms of the contract, not the number of assertions. If a case requires a concept you have not recalled, revisit the relevant lesson before opening the solution.

Your turn

Exercise

Checkpoint: Request State: practise

Write reduce for start(request) and finish(request,title). Start 1, then start 2. Finishing 1 must return the same loading object; finishing 2 must produce ready with title Types. Print true and Types.

Your workspaceTypeScript
// Define state and action unions, narrow before comparing ids, and keep stale events as no-ops.

Enable JavaScript to edit and type-check this example.

Hint 1: the idea

A matching identity is a runtime requirement beyond the action shape.

Hint 2: a first step

In finish, check state.tag and state.request before constructing ready.

Show one solution
One possible solutionTypeScript
type State =
  | { tag: "idle" }
  | {
      tag: "loading";

      request: number;
    }
  | {
      tag: "ready";

      request: number;

      title: string;
    };

type Action =
  | {
      type: "start";

      request: number;
    }
  | {
      type: "finish";

      request: number;

      title: string;
    };

function reduce(state: State, action: Action): State {
  if (action.type === "start") {
    return { tag: "loading", request: action.request };
  }

  if (state.tag !== "loading" || state.request !== action.request) {
    return state;
  }

  return { tag: "ready", request: action.request, title: action.title };
}

let state = reduce({ tag: "idle" }, { type: "start", request: 1 });

state = reduce(state, { type: "start", request: 2 });
console.log(
  reduce(state, { type: "finish", request: 1, title: "Old" }) === state
);
state = reduce(state, { type: "finish", request: 2, title: "Types" });
console.log(state.tag === "ready" ? state.title : "Unexpected state");

Enable JavaScript to edit and type-check this example.

true
Types

Explain why the implementation meets the stated contract. Which part is enforced by the language or checker, and which part needs behavioral evidence?

Change it and test it

Test finish while idle and duplicate finish after ready. Add an error completion with the same identity rule and test stale failure as well as stale success. Explain why declaring request:number does not make the stale guard unnecessary.

Was this article helpful?

Your response is saved on this device.