Typing functions

TypeScript · Essentials · 12 min

Functions are the main boundaries in any program, and their signatures are where TypeScript catches most mistakes.

Annotate each parameter; the return type is inferred but worth stating on exported functions:

function formatPrice(amount: number, currency: string = "EUR"): string { ... }

Parameter options:

  • Optional: name?: string — the parameter may be omitted and has type string | undefined inside.
  • Default: currency = "EUR" — also optional for callers, and the type is inferred from the default.
  • Rest: ...values: number[] collects remaining arguments into an array.

TypeScript checks calls strictly: too few or too many arguments is an error, unlike plain JavaScript.

A function type describes a callback or a function-valued variable: (item: string, index: number) => boolean. The parameter names are only documentation; the types and positions matter. A callback with fewer parameters is fine where more are offered — that's why arr.forEach(x => ...) works even though forEach passes three arguments.

Return types worth knowing:

  • void — the function returns nothing meaningful. A callback type returning void may be given a function that returns something; the value is just ignored.
  • never — the function never returns normally (it always throws or loops forever).

Arrow functions and function expressions are typed the same way. When a function is passed directly as an argument, its parameters are contextually typed, so you usually don't annotate them there.

The this parameter is a fake first parameter (function (this: HTMLElement) {}) that declares what this must be; it is erased in the output.

Example

function formatPrice(amount: number, currency = "EUR", note?: string): string {
  const text = `${amount.toFixed(2)} ${currency}`;
  return note ? `${text} (${note})` : text;
}

function sum(...values: number[]): number {
  return values.reduce((a, b) => a + b, 0);
}

// A function type for a callback
type Predicate = (value: string, index: number) => boolean;
const nonEmpty: Predicate = value => value.length > 0; // fewer params is fine

function fail(message: string): never {
  throw new Error(message);
}

console.log(formatPrice(9.5), formatPrice(3, "USD", "sale"), sum(1, 2, 3));
console.log(["a", "", "b"].filter(nonEmpty));
// @ts-expect-error -- Expected 1-3 arguments, but got 0
formatPrice();
if (Math.random() > 2) fail("unreachable");

Remember

  • Always annotate parameters; state return types on exported functions
  • p?: T is optional (T | undefined inside); defaults make parameters optional too
  • Function types look like (a: A, b: B) => R
  • void = no useful return value; never = never returns

Watch out for

  • Writing (string) => void — that declares a parameter named string of type any.

Check yourself

  1. A callback type is (value: string, index: number) => void. Is the function (v: string) => v.length assignable to it?
  2. Inside function f(label?: string), what is the type of label?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.typescriptlang.org/docs/handbook/2/functions.html