Skip to content

APPLICATION DEVELOPMENT · LESSON 16 OF 24

Promises, async/await and result types

Model asynchronous work and handle expected failures explicitly.

  • Promise
  • async
  • await
  • Result

Your runnable checkpoint

Extract into a new practice folder. Each download includes complete source, package.json, tsconfig.json and an offline lesson. Use Node.js 22 or 24. Each checkpoint is independent.

Install with npm install. Run npm run check, then npm run build. Terminal lessons use npm start; browser lessons use npm run serve and http://127.0.0.1:4173.

1. Understand the concepts

An async function always returns a Promise. await obtains its fulfilled value or throws its rejection reason. A Promise<T> describes the fulfilled value; it does not encode which exceptions might be thrown.

A discriminated Result<T> union can represent expected application failures as data. This forces callers to inspect ok before using value. Unexpected programming errors can still throw and should be handled at an appropriate boundary.

Promise.all starts from promises you provide and waits for all of them, rejecting when one rejects. Use it for independent work; use sequential await when one operation depends on another. Async does not make CPU-heavy calculations run on another thread.

This lesson uses a local async fixture so the download runs without a third-party network service. The same typing applies when a real database or HTTP client supplies the value.

2. Build and run

These are complete files. Preserve the paths shown below, including the src directory. The package and compiler configuration are included at the end.

src/main.ts

src/main.ts
type Result<T> = { ok: true; value: T } | { ok: false; error: string };
async function loadName(id: number): Promise<Result<string>> {
  await Promise.resolve();
  return id === 1 ? { ok: true, value: "Asha" } : { ok: false, error: "Not found" };
}
const results = await Promise.all([loadName(1), loadName(99)]);
for (const result of results) {
  console.log(result.ok ? result.value : result.error);
}

package.json

package.json
{
  "name": "edupoly-typescript-promises-and-async-await",
  "private": true,
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "check": "tsc --noEmit",
    "build": "tsc",
    "start": "node dist/main.js"
  },
  "devDependencies": {
    "typescript": "5.9.3"
  }
}

tsconfig.json

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "lib": [
      "ES2022",
      "DOM"
    ],
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noEmitOnError": true,
    "verbatimModuleSyntax": true,
    "rootDir": "src",
    "outDir": "dist",
    "sourceMap": true,
    "types": [],
    "moduleDetection": "force"
  },
  "include": [
    "src/**/*"
  ]
}

3. Trace the code and check the result

  1. The generic result carries a string only when ok is true.
  2. Top-level await works because this file is an ES module.
  3. Promise.all preserves the input order of results.

Expected result

Asha
Not found

4. Practice independently

Return a user object instead of a name while preserving the same error case.

Show the explanation

Define type User = { id: number; name: string } and return Promise<Result<User>>. Access result.value.name only inside the ok branch.

When something goes wrong

If tsc is missing, run npm install in the folder containing package.json. If the output looks old, rebuild before running. Read the first compiler diagnostic and check the indicated field or argument. For module errors, preserve the .js extensions in imports. Browser examples must run through the included local server, not directly as TypeScript files.

Read the related official TypeScript documentation ↗