Typed Promises

The Course
•
4 min read

Before today, revisit JavaScript intermediate’s promises and async/await. A Promise<T> describes a future fulfillment value T. An async function returning a number produces Promise<number>, not number immediately.

Await gives you the fulfilled value or throws a rejection. TypeScript describes the fulfillment type; it does not restrict a promise to rejecting with one declared error type. Catch values therefore remain unknown under strict checking. A Result<T> can make expected failure part of the fulfilled value when that is the contract you choose.

These TypeScript editors run isolated ES modules. They support top-level await, so the example explicitly awaits completion before the runner ends. This differs from the older JavaScript editor’s main() entry convention. For an async helper, end a program with await main() if it must finish before output is collected. Unawaited background work is not kept alive by the runner.

A type annotation does not initiate network I/O or make a synchronous computation concurrent. Our examples use deterministic local promises, and normal completion must fit the playground’s three-second execution limit.

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
async function countBooks(): Promise<number> {
  return 2;
}

const pending = countBooks();
const count = await pending;

console.log(count + 1);

Enable JavaScript to edit and type-check this example.

Check your prediction
3

Read the compiler error

An async return is a promise; the fulfillment value is not available yet.

Find and fix the type errorTypeScript
async function count(): Promise<number> {
  return 2;
}

const total: number = count();

console.log(total);

Enable JavaScript to edit and type-check this example.

Check your diagnosis

Await count() before assigning it to a number. This editor is an ES module, so top-level await is supported.

Type the future value and await its availability

Promise<T> describes fulfillment data, not an immediately available T. Calling an async function begins executing its body and returns a promise; await obtains its value or throws its rejection. The type annotation does not initiate a network request or make CPU work parallel.

A fulfillment type also does not restrict rejection reasons. Use a Result when expected failure should be part of fulfilled data, while still recognizing that programming errors can reject the function. Keep those two failure policies distinct.

Type the future value and await its availability: traceTypeScript
async function load(): Promise<number> {
  return 0;
}

const pending = load();

console.log(typeof pending.then);

const value = await pending;

console.log(value + 1);

Enable JavaScript to edit and type-check this example.

Check your prediction
function
1

The promise offers coordination methods; the awaited number offers arithmetic. Assigning pending directly to a number would lose the availability distinction and must fail checking.

Type the future value and await its availability: contrastTypeScript
type Result<T> =
  | {
      ok: true;

      value: T;
    }
  | {
      ok: false;

      message: string;
    };

async function load(): Promise<Result<number>> {
  return { ok: false, message: "Expected failure" };
}

const result = await load();

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

Enable JavaScript to edit and type-check this example.

Check your prediction
Expected failure

This call fulfills with a failure variant rather than rejecting. A caller handles it by narrowing the result; a catch alone would not observe this expected failure.

Try the rule on new inputs

Compare a thrown Error with a fulfilled error variant. State what await does for each. Explicitly await the intended work in this module editor so its output is collected before the run ends.

Your turn

Exercise

Typed Promises: practise

Write an async label function accepting a title:string and returning Promise<string>. Await it for Types and print Book: Types. Do not cast the promise to a string.

Your workspaceTypeScript
// Use top-level await in this TypeScript module editor.

Enable JavaScript to edit and type-check this example.

Hint 1: the idea

The async function returns a promise even when its body returns text directly.

Hint 2: a first step

Declare Promise<string> as the return and await the call before logging.

Show one solution
One possible solutionTypeScript
async function label(title: string): Promise<string> {
  return `Book: ${title}`;
}

console.log(await label("Types"));

Enable JavaScript to edit and type-check this example.

Book: 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

Try calling toUpperCase on the unawaited promise and read the compiler error. Then await it and call the method on the string. Explain why Promise<string> tells you nothing about the exact rejection value.

Was this article helpful?

Your response is saved on this device.