Checkpoint: Typed Loader

The Course
•
4 min read

Rebuild a small typed loader from the ideas above. A transport returns Promise<unknown>. The loader waits for it, handles transport failure with a stable message, validates a numeric payload, and returns Result<number>.

A factory createLoader(transport) returns a zero-argument async function. This keeps dependency configuration separate from each call and makes a fixture easy to substitute in tests. The signature should preserve the difference between calling the factory and calling the returned loader.

Test a success with zero, a successful-but-invalid string payload and a rejected transport. Do not use any, assertions or a broad catch that labels every possible programming bug as a transport failure. The final project replaces this numeric decoder with the catalogue decoder from Day 4.

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 Loader = () => Promise<number>;

function createLoader(value: number): Loader {
  return async () => value;
}

const load = createLoader(25);

console.log(await load());

Enable JavaScript to edit and type-check this example.

Check your prediction
25

Acceptance cases before you code

Write a small test plan before implementing typed loader boundaries. 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
Valid zero transport dataProduce success containing zero
Successful malformed dataProduce the domain failure
Transport rejectionProduce the transport failure without pretending data was validated

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: Typed Loader: practise

Write createLoader(transport:()=>Promise<unknown>) returning ()=>Promise<Result<number>> with finite nonnegative integer validation. Print 0, Invalid count and Unavailable for the three scenarios described above.

Your workspaceTypeScript
// Keep transport invocation inside the narrow try block and validation afterward.

Enable JavaScript to edit and type-check this example.

Hint 1: the idea

The factory returns a function; it does not fetch during configuration.

Hint 2: a first step

Define the returned function as async and return its result variants.

Show one solution
One possible solutionTypeScript
type Result<T> =
  | {
      ok: true;

      value: T;
    }
  | {
      ok: false;

      message: string;
    };

function createLoader(
  transport: () => Promise<unknown>
): () => Promise<Result<number>> {
  return async () => {
    let value: unknown;

    try {
      value = await transport();
    } catch {
      return { ok: false, message: "Unavailable" };
    }

    if (
      typeof value !== "number" ||
      !Number.isFinite(value) ||
      !Number.isInteger(value) ||
      value < 0
    ) {
      return { ok: false, message: "Invalid count" };
    }

    return { ok: true, value };
  };
}

for (const transport of [
  async () => 0,
  async () => "0",
  async () => {
    throw "offline";
  }
]) {
  const load = createLoader(transport);
  const result = await load();

  console.log(result.ok ? result.value : result.message);
}

Enable JavaScript to edit and type-check this example.

0
Invalid count
Unavailable

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

Count calls to prove creating a loader does not invoke the transport and each load invokes it once. Add runtime assertions for every result branch. A static Promise<Result<number>> signature cannot guarantee that your decoder checks every domain rule.

Was this article helpful?

Your response is saved on this device.