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

Typing function signatures, callbacks, overloads, and rest parameters.

Type a function

Annotate parameters and return values.

typescriptANYtypescriptfunctions
typescript
function add(a: number, b: number): number {
  return a + b;
}
Notes

Return annotations can improve clarity even when TypeScript can infer them.

Use optional parameters

Mark a parameter as optional with `?`.

typescriptANYtypescriptfunctionsoptional-parameters
typescript
function greet(name: string, prefix?: string) {
  return `${prefix ?? "Hello"}, ${name}`;
}
Notes

Optional parameters become `T | undefined` in the body.

Use default parameters

Provide a default value and keep a concrete type.

typescriptANYtypescriptfunctionsdefault-parameters
typescript
function connect(host = "localhost", port = 5432) {
  return `${host}:${port}`;
}
Notes

Default parameters often remove the need for manual undefined handling.

Use rest parameters

Accept a variable number of arguments.

typescriptANYtypescriptfunctionsrest-parameters
typescript
function joinWith(separator: string, ...parts: string[]) {
  return parts.join(separator);
}
Notes

Rest parameters are typed as arrays.

Use a function type alias

Describe callback signatures explicitly.

typescriptANYtypescriptfunctionscallbacks
typescript
type Predicate<T> = (value: T) => boolean;
Notes

Function type aliases keep callback signatures reusable and readable.

Declare function overloads

Expose multiple call signatures with one implementation.

typescriptANYtypescriptfunctionsoverloads
typescript
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);
}
Notes

Overloads help when parameter/return relationships are difficult to express with unions alone.

Narrowing and Control Flow

Use runtime checks so TypeScript can refine types safely.

Narrow with `typeof`

Refine primitive unions using runtime checks.

typescriptANYtypescriptnarrowingtypeof
typescript
function printId(id: string | number) {
  if (typeof id === "string") {
    console.log(id.toUpperCase());
  } else {
    console.log(id.toFixed(0));
  }
}
Notes

TypeScript uses control flow analysis to narrow `id` in each branch.

Narrow with `instanceof`

Refine class-based unions using constructor checks.

typescriptANYtypescriptnarrowinginstanceof
typescript
function formatValue(value: Date | Error) {
  if (value instanceof Date) {
    return value.toISOString();
  }
  return value.message;
}
Notes

`instanceof` works well when runtime classes are available.

Narrow with the `in` operator

Refine unions by checking property existence.

typescriptANYtypescriptnarrowingin-operator
typescript
type Dog = { bark(): void };
type Cat = { meow(): void };

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

Property checks are especially useful for discriminating object-shape unions.

Exhaustive switch with `never`

Catch unhandled union members.

typescriptANYtypescriptneverexhaustive-switch
typescript
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;
    }
  }
}
Notes

The `never` assignment fails if a new union member is added but not handled.

Custom type guard with predicate

Teach TypeScript a reusable narrowing rule.

typescriptANYtypescripttype-guardstype-predicate
typescript
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;
};
Notes

Type predicates are useful for validation boundaries like JSON input or external APIs.

Async and Promise Types

Type async functions, promises, and result wrappers.

Type an async function

Return a typed promise from an async function.

typescriptANYtypescriptasyncpromises
typescript
async function fetchName(): Promise<string> {
  return "Ada";
}
Notes

Async functions always return a `Promise`, even when returning a plain value.

Use `Promise<T>`

Annotate promise-returning APIs explicitly.

typescriptANYtypescriptpromises
typescript
function wait(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}
Notes

Useful for utilities, service functions, and browser or Node wrappers.

Model a result union

Represent success and failure without throwing.

typescriptANYtypescriptresultsunions
typescript
type Ok<T> = { ok: true; value: T };
type Err = { ok: false; error: string };
type Result<T> = Ok<T> | Err;
Notes

Result unions are a clean pattern for app code that wants explicit error handling.

Recommended next

No recommendations yet.