Advanced

Generics

Write reusable, type-safe functions with generic type parameters that preserve types through calls.

What you will see

2 2

About 2 min of reading

Quick check

Before this new lesson, one small question about the lesson you just finished. Recall it, do not look it up.

Try It

A generic function preserves the caller's type through its body and return value, so no casting is needed. Type parameters are inferred from the arguments at the call site.

1

A generic identity function

function identity(arg) { return arg; } const n = identity(42); const s = identity("hi"); console.log(n, s);

Before you run it, say out loud what it will print. Then open the answer.

What it prints
42 hi
2

Generic functions on arrays

function first(items) { return items[0]; } const list = [1, 2, 3]; console.log(first(list));

Before you run it, say out loud what it will print. Then open the answer.

What it prints
1
3

Generic constraints

function length(value) { return value.length; } console.log(length("hi"), length([1, 2]));

Before you run it, say out loud what it will print. Then open the answer.

What it prints
2 2

Put it in order

Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.

Data Flow

A generic function receives a type parameter, threads it through inputs and outputs, and preserves it for the caller.
1 Call with an explicit or inferred type T
↓
2 Function body treats values as T
↓
3 Return value carries the same T
↓
4 Caller gets a fully typed result back
Why this works
Generics on classes and interfaces

Classes can be generic too:

class Box<T> {
  constructor(public value: T) {}
  get(): T { return this.value; }
}
const b = new Box<number>(10);
b.get();   // number

The class stores and returns T, so a Box<number> only ever holds numbers. Generic interfaces constrain method contracts the same way.

When you have a choice

Why generics instead of any

any loses type information; generics preserve it:

function wrap1<T>(v: T): T[] { return [v]; }   // preserves type
function wrap2(v: any): any[] { return [v]; }   // any everywhere

wrap1([1,2,3]) returns number[][], so later use stays typed. wrap2 returns any[], silently removing all safety. Prefer generics whenever you can.

Examples

A generic identity function

Preserve the input type

function identity<T>(arg: T): T {
  return arg;
}
const n = identity<number>(42);   // n is number
const s = identity<string>("hi"); // s is string

<T> declares a type parameter. Whatever type you pass in is the same type returned, so no information is lost.

Generic functions on arrays

Typed collection helpers

function first<T>(items: T[]): T | undefined {
  return items[0];
}
const list = [1, 2, 3];
const head = first(list);   // number | undefined

The generic T is inferred from the argument, so first(list) returns number | undefined automatically.

Generic constraints

Restricting types

function length<T extends { length: number }>(value: T): number {
  return value.length;
}
length("hi");   // ok: strings have length
length([1, 2]);  // ok: arrays have length

extends { length: number } constrains T to anything with a length property, letting you safely use .length inside.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Unions and Type Guards
Previous Lesson
Unions and Type Guards
Next Lesson
Mapped and Conditional Types
Mapped and Conditional Types →