Generics let you write code that works with different types. Think of a box—sometimes you put toys in it, sometimes fruit. The box works the same way regardless of what’s inside.
Without generics, you’d need separate boxes for each type:
interface ToyBox {
contents: Toy;
}
interface FruitBox {
contents: Fruit;
}
// This gets repetitive fast...
One box that works with any type. The <T> is a placeholder for whatever type you specify later:
interface Box<T> {
contents: T;
}
// A box of toys
const toyBox: Box<Toy> = { contents: { name: "Lego" } };
// A box of fruit
const fruitBox: Box<Fruit> = { contents: { name: "Apple" } };
Same idea with functions. Write it once, use it with any type:
function getFirstItem<T>(items: T[]): T {
return items[0];
}
const firstNumber = getFirstItem([1, 2, 3]); // type: number
const firstWord = getFirstItem(["a", "b", "c"]); // type: string
TypeScript infers the type from what you pass in.
A reusable container class:
class Box<T> {
private contents: T;
constructor(item: T) {
this.contents = item;
}
getContents(): T {
return this.contents;
}
replaceContents(newItem: T): void {
this.contents = newItem;
}
}
const toyBox = new Box({ name: "Lego", pieces: 500 });
console.log(toyBox.getContents()); // { name: "Lego", pieces: 500 }
const fruitBox = new Box({ name: "Apple", ripe: true });
console.log(fruitBox.getContents()); // { name: "Apple", ripe: true }
Sometimes you need to limit what types are allowed. Use extends to say “only types that have these properties”:
interface HasName {
name: string;
}
// Only accept items that have a name property
function labelBox<T extends HasName>(item: T): string {
return `Box contains: ${item.name}`;
}
labelBox({ name: "Lego", pieces: 500 }); // "Box contains: Lego"
labelBox({ name: "Apple" }); // "Box contains: Apple"
labelBox({ color: "red" }); // Error: missing 'name' property
Set a fallback type if none is specified:
interface Box<T = string> {
contents: T;
}
const stringBox: Box = { contents: "hello" }; // T defaults to string
const numberBox: Box<number> = { contents: 42 }; // T is number