TS TypeScript Basics

Add type safety to JavaScript with practical TypeScript patterns and compiler habits.

TS Async Programming

TypeScript Basics Lesson 36 of 50 ~13 min read

Overview

Type promises, async functions, API responses, errors, and async helpers.

TS Async Programming helps you describe intent in code so mistakes are caught earlier. Treat types as documentation that the compiler can verify, not as noise added after the fact.

Core Ideas

  • Use TS Async Programming to make data shapes and function contracts explicit.
  • Prefer precise types over broad ones, especially at module boundaries.
  • Narrow unknown input before using it.
  • Let compiler errors guide better modeling instead of disabling checks.

Step by Step

  1. Write the JavaScript shape that TS Async Programming describes.
  2. Add the narrowest useful type annotation.
  3. Use the value in a function so the compiler can verify the contract.
  4. Intentionally break the shape once, then read the compiler error.

Beginner Explanation

TS Async Programming teaches how functions communicate: what input they accept, what they return, and what errors or promises they may produce.

Typed functions are especially useful because most bugs happen when one part of the code calls another part with the wrong shape.

Beginners should type parameters first, then return values when the function boundary matters.

Before You Start

  • Before practicing TS Async Programming, write the JavaScript value or function you want to describe.
  • Identify the boundary: function parameter, return value, object shape, API response, component prop, or configuration option.
  • Turn on strict compiler settings when possible so weak assumptions become visible.
  • Change one type at a time and read the compiler message before fixing it.
  • Remember that TypeScript checks code before runtime; runtime validation is still needed for untrusted external data.

Key TypeScript Concepts

  • Function parameters should describe required, optional, default, and rest inputs.
  • Return types matter most for exported functions and public APIs.
  • Callbacks should include parameter and return expectations.
  • Promise<T> describes the resolved value of async work.

Plain-English Glossary

  • Type annotation: a written type such as string, number, Lesson, or Promise<User>.
  • Inference: TypeScript deciding a type from the value or usage.
  • Union: a type that allows one of several choices.
  • Narrowing: checking a value so TypeScript can treat it as a more specific type.
  • Generic: a reusable type parameter such as T that preserves information.
  • Interface: an extendable object contract.
  • Type alias: a name for any type expression, including unions and object shapes.
  • tsconfig: the project file that controls compiler behavior.

What You Will Learn

  • Explain what kind of mistake TS Async Programming helps TypeScript catch.
  • Write a small typed example and predict whether the compiler should accept or reject it.
  • Read at least one compiler error and identify the expected type and actual type.
  • Refactor the example so the type describes the real data more clearly.

Where You Use This in Real Projects

You use TS Async Programming in forms, API clients, React components, Node.js services, configuration files, data models, reusable utilities, validation layers, tests, and shared packages.

TypeScript is most valuable at boundaries where values move between files, functions, packages, APIs, users, databases, and UI components.

A practical team uses TypeScript to reduce accidental misuse, improve editor autocomplete, and make refactoring less risky.

Compiler and Tooling Notes

  • Use strict mode for new code when possible.
  • Do not turn a compiler error into any unless you understand what safety is being removed.
  • Remember that TypeScript types disappear at runtime after compilation.
  • Validate JSON, form data, URL params, and external API responses at runtime before trusting them.
  • When a type becomes hard to read, split it into named aliases and test it with small examples.

Beginner Mental Model

Think of TS Async Programming as a contract between the function author and every caller.

The parameter types describe what the function needs, and the return type describes what the caller can trust after it runs.

Common Compiler Errors and Fixes

  • Type mismatch: the value used in TS Async Programming does not match the expected type. Fix the value or correct the type model.
  • Missing property: an object does not include a required field. Add the field, make it optional only if it is truly optional, or use a different type.
  • Possibly null or undefined: check the value before using it, use optional chaining, or provide a fallback.
  • Unsafe any: replace any with a specific type or unknown plus a narrowing check.
  • No matching overload or call signature: compare the arguments you passed with the function type and adjust the call.

Real-World Pattern

type Validator<T> = (value: unknown) => value is T;

function parseWith<T>(value: unknown, validator: Validator<T>): T | null {
  return validator(value) ? value : null;
}
  • This TS Async Programming pattern uses a small type at a real boundary instead of adding types everywhere at random.
  • The example keeps runtime code readable while giving the compiler enough information to catch mistakes.
  • You can reuse the same pattern in API clients, form handlers, components, services, tests, and shared utilities.

Code Example

type Formatter = (value: string) => string;

function formatLesson(title: string, minutes = 30, formatter?: Formatter): string {
  const safeTitle = formatter ? formatter(title) : title.trim();
  return `${safeTitle} takes ${minutes} minutes.`;
}

async function loadTitle(): Promise<string> {
  return 'Async TypeScript';
}

Another Example

type Result<T> =
  | { ok: true; data: T }
  | { ok: false; error: string };

async function loadLesson(slug: string): Promise<Result<string>> {
  if (!slug) return { ok: false, error: 'Missing slug' };
  return { ok: true, data: slug.toUpperCase() };
}

More Practice Examples

Example 1: Model lesson data

type Level = 'beginner' | 'intermediate' | 'advanced';

interface Lesson {
  slug: string;
  title: string;
  level: Level;
  minutes: number;
}

const lesson: Lesson = {
  slug: 'ts-practice',
  title: 'TypeScript Practice',
  level: 'beginner',
  minutes: 30
};
  • The Level union prevents accidental values such as easy or expert.
  • The Lesson interface names the object shape once so it can be reused.
  • The compiler checks missing properties and wrong value types before runtime.

Example 2: Narrow unknown input

function formatTitle(value: unknown): string {
  if (typeof value === 'string' && value.trim() !== '') {
    return value.trim();
  }

  return 'Untitled lesson';
}

console.log(formatTitle('  TS Functions  '));
  • unknown forces the function to check the value before using string methods.
  • The return type tells callers they will always receive a string.
  • This pattern is useful for form input, JSON, and third-party data.

Example 3: Reuse a generic response type

type ApiResult<T> =
  | { ok: true; data: T }
  | { ok: false; error: string };

function unwrap<T>(result: ApiResult<T>): T {
  if (!result.ok) throw new Error(result.error);
  return result.data;
}
  • The generic T preserves the data type through the helper.
  • The union makes success and failure explicit.
  • The if statement narrows result before data is returned.

Beginner Practice Examples

Practice: Type a callback

type Lesson = { title: string; minutes: number };
type Filter = (lesson: Lesson) => boolean;

function findLessons(lessons: Lesson[], filter: Filter): Lesson[] {
  return lessons.filter(filter);
}
  • The callback receives one Lesson and must return true or false.
  • The returned array still contains Lesson objects.
  • This pattern appears in filters, event handlers, validators, and mappers.

Practice: Add a safe formatter

function minutesLabel(minutes: number | null | undefined): string {
  if (minutes == null) {
    return 'Time not set';
  }

  return `${minutes} minutes`;
}

console.log(minutesLabel(35));
  • The parameter allows number, null, or undefined.
  • The null check handles both null and undefined.
  • After the check, TypeScript knows minutes is a number.

Practice: Type a small API result

type Result<T> =
  | { ok: true; data: T }
  | { ok: false; error: string };

const result: Result<string[]> = {
  ok: true,
  data: ['HTML', 'CSS', 'TypeScript']
};
  • The generic result can work with any data type.
  • The ok field acts as the discriminant for narrowing.
  • Failure results must include error instead of data.

Compiler Error Practice

Error 1: Wrong value type

Broken version

type Lesson = {
  title: string;
  minutes: number;
};

const lesson: Lesson = {
  title: 'TS Basics',
  minutes: '30'
};

Fixed version

type Lesson = {
  title: string;
  minutes: number;
};

const lesson: Lesson = {
  title: 'TS Basics',
  minutes: 30
};
  • The broken version uses a string where the type requires a number.
  • The fixed version changes the value instead of weakening the type.
  • This is the kind of mistake TS Async Programming should catch before runtime.

Error 2: Possibly undefined value

Broken version

const lessons = ['HTML', 'CSS'];
const first = lessons[2];

console.log(first.toUpperCase());

Fixed version

const lessons = ['HTML', 'CSS'];
const first = lessons[2];

if (first) {
  console.log(first.toUpperCase());
} else {
  console.log('No lesson found');
}
  • The array lookup may not find an item.
  • The fixed version checks the value before using a string method.
  • This habit helps with arrays, maps, query selectors, and API results.

Error 3: Unsafe unknown value

Broken version

function printTitle(value: unknown) {
  console.log(value.trim());
}

Fixed version

function printTitle(value: unknown) {
  if (typeof value === 'string') {
    console.log(value.trim());
    return;
  }

  console.log('Untitled');
}
  • unknown cannot be used like a string until it is narrowed.
  • The typeof check proves the value is a string.
  • Use this pattern whenever data comes from outside your typed code.

Example Explained

  • The TS Async Programming example starts with a real JavaScript value, function, object, or project setting.
  • The type describes the smallest useful contract for that value.
  • The compiler can then compare how the value is created, passed, and used.
  • When the example narrows a value, TypeScript allows safer access only after the check.
  • The best examples keep runtime behavior and type behavior easy to explain separately.

How to Read This Example

  1. Find the value, function, class, object, or configuration option being typed.
  2. Read the type from left to right and name what values are allowed.
  3. Look for optional properties, unions, generics, or narrowing checks.
  4. Change one property or argument to the wrong type and predict the compiler error.
  5. Fix the TS Async Programming example by improving the type model, not by hiding the error with any.

Checklist

  • Let the compiler guide the design instead of silencing errors.
  • Prefer unknown over any when input is not trusted yet.
  • Keep shared types close to the data they describe.

Common Mistakes

  • Using any to silence errors instead of modeling the data.
  • Typing only the easy parts and leaving unsafe boundaries unchecked.
  • Duplicating types instead of deriving them with utility types or generics.

Do and Don't

  • Do: use TS Async Programming to describe real data and real function contracts.
  • Do: prefer unknown over any when data has not been checked yet.
  • Do: let inference help with local variables when the value is obvious.
  • Don't: add complicated types that make the code harder to use than the bug they prevent.
  • Don't: use type assertions to silence errors before proving the value is safe.

Practice Challenge

Type the TS Async Programming example, intentionally break one value, and read the compiler message before fixing it.

Try These Changes

  • Add one missing property to the example and read the compiler error.
  • Change one string literal to an invalid value and explain why TypeScript rejects it.
  • Replace any with unknown, then add a narrowing check.
  • Extract an inline object type into a named interface or type alias.
  • Write one function that accepts the type and returns a formatted display string.

Step-by-Step Project Path

  1. Create one small file named after TS Async Programming and write a plain JavaScript version first.
  2. Add the first type at the boundary: function parameter, object shape, API result, component props, or config value.
  3. Intentionally break one value so you can read the compiler error in context.
  4. Fix the value or improve the type without using any as a shortcut.
  5. Add one runtime check if the value could come from a user, API, URL, file, or third-party package.
  6. Write a short comment for yourself explaining which bug the type prevents.

Quick Check

  • Question: Do TypeScript types exist at runtime? Answer: No, they are checked before the JavaScript runs.
  • Question: Why is unknown safer than any? Answer: unknown requires narrowing before you use the value.
  • Question: What should you type first? Answer: Boundaries such as parameters, return values, API data, and props.
  • Question: What is inference? Answer: TypeScript deciding a type from the value or context.
  • Question: How should you fix most TS Async Programming errors? Answer: Adjust the value or model the type more accurately.

Debugging Checks

  • Read the first compiler error carefully before changing multiple files.
  • Identify the expected type and the actual type in the error message.
  • Hover values in the editor to inspect inferred types.
  • Check tsconfig strictness, module settings, lib settings, and included files when errors seem surprising.
  • Use small temporary variables to make complicated inferred types easier to inspect.

Mini Project

Build a typed API helper for TS Async Programming: typed parameters, return type, callback, async Promise result, and explicit error result.

Mastery Check

  • You can explain what error TS Async Programming catches before runtime.
  • You can narrow unknown data before using it.
  • You can reuse the type safely in another function or module.
Create a free account to save which lessons you've finished. Save my progress