BeCuriouzLearn something new every day
Generic Interfaces & Classes: Reusable Containers
💡 Attaching a <T> to an interface or class turns it into one reusable, type-safe blueprint that specializes to any concrete type on use.
In earlier cards you saw how a generic function captures a type with a parameter like . That same idea scales up: you can attach a type parameter to an entire interface or class, turning it into a reusable blueprint that works for any type while keeping full type safety.
A generic interface declares its type parameter right after the name. Here Box holds a value of whatever type you plug in — Box forces .value to be a number, Box forces a string:
interface Box<T> {
value: T;
}
const n: Box<number> = { value: 42 };
const s: Box<string> = { value: "hi" };A generic class works the same way — the after the class name is in scope across every field and method, so the type flows through consistently:
class Stack<T> {
private items: T[] = [];
push(item: T): void { this.items.push(item); }
pop(): T | undefined { return this.items.pop(); }
}
const nums = new Stack<number>();
nums.push(10); // ok
const top = nums.pop(); // top: number | undefined
// nums.push("oops"); // ✗ compile errorBecause nums is a Stack, push only accepts numbers and pop returns number | undefined — no casting, no any. One definition, infinitely many concrete types.