Functions#

TypeScript inherits every JavaScript function form (see Functions) and adds typed parameters, typed returns, generic type parameters, overload signatures, this parameters, and type-narrowing return forms (x is T, asserts x is T).

Signatures#

A signature is parameters plus a return type.

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

const add2 = (a: number, b: number): number => a + b;

type AddFn = (a: number, b: number) => number;
const add3: AddFn = (a, b) => a + b;     // params inferred from AddFn

The operator annotates exported signatures; for locals, inference is usually enough.

Optional and default parameters#

? marks a parameter optional (the type includes undefined); = provides a default and the parameter type narrows to the non-undefined form.

function greet(name: string = "operator"): string {
  return `hello, ${name}`;
}

function listen(host: string, port?: number): void {
  // port: number | undefined
}

Optional parameters must come after required ones; for a more flexible API, use an options object.

function fetchJson(url: string, opts: {timeout?: number; signal?: AbortSignal} = {}) {
  const {timeout = 5000, signal} = opts;
}

Rest parameters#

...args: T[] collects trailing arguments.

function log(level: "info" | "warn" | "error", ...msg: unknown[]) {
  console.error(`[${level}]`, ...msg);
}

Overloads#

Multiple signatures followed by one implementation. The implementation signature is invisible to callers; they see only the overloads.

function len(x: string): number;
function len<T>(x: T[]): number;
function len(x: string | unknown[]): number {
  return x.length;
}

len("hi");           // number
len([1, 2, 3]);      // number
len(42);             // error: no matching overload

Generics#

<T> declares a type parameter. The caller (or inference) supplies the actual type.

function head<T>(xs: T[]): T | undefined {
  return xs[0];
}

const n = head([1, 2, 3]);            // n: number | undefined

Constraints with extends.

function pluck<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

pluck({name: "rk", age: 30}, "name");   // string
pluck({name: "rk", age: 30}, "xyz");    // error

Default type parameters.

function load<T = unknown>(): Promise<T> {
  // ...
}

Type guards#

A function whose return type is x is T narrows the argument when called inside a condition.

function isString(x: unknown): x is string {
  return typeof x === "string";
}

function isUser(x: unknown): x is User {
  return typeof x === "object" && x !== null
    && "id" in x && typeof (x as any).id === "string";
}

if (isString(x)) x.toUpperCase();         // x narrowed to string

Assertion signatures#

asserts returns void but proves the assertion to the compiler. After the call, the binding is narrowed.

function assert(cond: unknown, msg = "assertion failed"): asserts cond {
  if (!cond) throw new Error(msg);
}

function assertString(x: unknown): asserts x is string {
  if (typeof x !== "string") throw new TypeError("not a string");
}

function trim(x: unknown): string {
  assertString(x);
  return x.trim();                        // x narrowed to string here
}

this parameter#

A first parameter named this (with a type) tells the compiler what this must be at the call site. Erased at runtime.

interface User { name: string; }

function greet(this: User, prefix: string) {
  return `${prefix} ${this.name}`;
}

greet.call({name: "rk"}, "hi");          // works
greet("hi");                              // error: this is missing

Use this for hand-written callbacks where this binding matters (jQuery-style callbacks, certain testing frameworks).

Function-type aliases#

Capture a signature as a reusable type.

type Handler<Req, Res> = (req: Req) => Promise<Res>;

const get: Handler<Request, Response> = async (req) => new Response("ok");

type Predicate<T> = (x: T) => boolean;
const isAdult: Predicate<User> = u => u.age >= 18;

Async functions#

async functions return Promise<T>; the return-type annotation is the unwrapped type. Awaited<T> strips the promise wrapper.

async function load(id: string): Promise<User> {
  const r = await fetch(`/users/${id}`);
  if (!r.ok) throw new Error(`HTTP ${r.status}`);
  return r.json();
}

type LoadedUser = Awaited<ReturnType<typeof load>>;     // User

References#