TypeScript Functions and Narrowing

Function typing, overloads, control-flow narrowing, and async typing.

View
StandardDetailedCompact
Export
Copy the compact sheet, download it, or print it.
Download
`D` dense toggle · `C` copy all
## Functions and Parameters
Type a function
function add(a: number, b: number): number {
  return a + b;
}

# Annotate parameters and return values.

Use optional parameters
function greet(name: string, prefix?: string) {
  return `${prefix ?? "Hello"}, ${name}`;
}

# Mark a parameter as optional with `?`.

Use default parameters
function connect(host = "localhost", port = 5432) {
  return `${host}:${port}`;
}

# Provide a default value and keep a concrete type.

Use rest parameters
function joinWith(separator: string, ...parts: string[]) {
  return parts.join(separator);
}

# Accept a variable number of arguments.

Use a function type alias
type Predicate<T> = (value: T) => boolean;

# Describe callback signatures explicitly.

Declare function overloads
function format(value: number): string;
function format(value: Date): string;
function format(value: number | Date): string {
  return value instanceof Date ? value.toISOString() : value.toFixed(2);
}

# Expose multiple call signatures with one implementation.

## Narrowing and Control Flow
Narrow with `typeof`
function printId(id: string | number) {
  if (typeof id === "string") {
    console.log(id.toUpperCase());
  } else {
    console.log(id.toFixed(0));
  }
}

# Refine primitive unions using runtime checks.

Narrow with `instanceof`
function formatValue(value: Date | Error) {
  if (value instanceof Date) {
    return value.toISOString();
  }
  return value.message;
}

# Refine class-based unions using constructor checks.

Narrow with the `in` operator
type Dog = { bark(): void };
type Cat = { meow(): void };

function speak(animal: Dog | Cat) {
  if ("bark" in animal) animal.bark();
  else animal.meow();
}

# Refine unions by checking property existence.

Exhaustive switch with `never`
type State = "idle" | "loading" | "success";

function render(state: State) {
  switch (state) {
    case "idle":
      return "Idle";
    case "loading":
      return "Loading";
    case "success":
      return "Done";
    default: {
      const _exhaustive: never = state;
      return _exhaustive;
    }
  }
}

# Catch unhandled union members.

Custom type guard with predicate
type User = { id: number; name: string };

const isUser = (value: unknown): value is User => {
  return typeof value === "object" && value !== null && "id" in value && "name" in value;
};

# Teach TypeScript a reusable narrowing rule.

## Async and Promise Types
Type an async function
async function fetchName(): Promise<string> {
  return "Ada";
}

# Return a typed promise from an async function.

Use `Promise<T>`
function wait(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}

# Annotate promise-returning APIs explicitly.

Model a result union
type Ok<T> = { ok: true; value: T };
type Err = { ok: false; error: string };
type Result<T> = Ok<T> | Err;

# Represent success and failure without throwing.

Recommended next

No recommendations yet.