Generic Functions

The Problem Generics Solve

A function that returns the first element of any array could use any, but then the caller loses the element type. A type parameter <T> keeps the link between input and output.

Example

TypeScript infers T from the argument: number for the first call, string for the second.

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

const n = first([10, 20, 30]);       // number | undefined
const s = first(["UPI", "Card"]);    // string | undefined
console.log(n, s?.toLowerCase());

function pair<A, B>(a: A, b: B): [A, B] {
  return [a, b];
}
const p = pair("Pen", 20);           // [string, number]
console.log(p[0].length, p[1].toFixed(2));

Output

10 upi
3 20.00

Generics Keep Type Errors

Example

Because first returns number | undefined here, calling a string method on it fails (TS2339). With any this bug would be hidden.

function first<T>(items: T[]): T | undefined {
  return items[0];
}
console.log(first([1, 2])?.toUpperCase());

Compiler output (tsc)

Line 4: error TS2339: Property 'toUpperCase' does not exist on type 'number'.

Explicit Type Arguments

Example

You can also pass T yourself, for example for an empty array where nothing can be inferred.

function wrapInArray<T>(value?: T): T[] {
  return value === undefined ? [] : [value];
}

const ids = wrapInArray<number>();
ids.push(42);
console.log(ids, wrapInArray("x"));

Output

[ 42 ] [ 'x' ]