Generics
Advanced TypeScript•Section 1 of 4•advanced
Topic Overview
Take your time to understand each concept. Practice with the examples below!
Generics are like wildcards in TypeScript - they let you write flexible, reusable code that works with any type while keeping everything type-safe. Think of them as templates that get filled in later!
What are Generics? Generics are placeholders for types. Instead of writing separate functions for every type, you write one generic function that works with any type. The actual type is specified when you use the function.
The Problem Generics Solve:
Without generics, you'd need to either:
- •Write duplicate functions for each type (repetitive)
- •Use 'any' type (loses type safety)
- •Use union types (gets messy quickly)
Generics give you the best of both worlds!
Generic Syntax:
- •Basic:<T> is the type parameter (T is just a convention)
- •Multiple types:<T, U, V>
- •Constraints:<T extends SomeType>
- •Default types:<T = string>
Common Generic Patterns:
- •Generic functions:function identity<T>(arg: T): T
- •Generic interfaces:interface Box<T> { value: T }
- •Generic classes:class Container<T> { }
- •Generic type aliases:type Result<T> = T | Error
Real-World Use Cases:
- •Arrays:Array<T> can hold any type
- •Promises:Promise<T> resolves to any type
- •React components:Component<Props, State>
- •API responses:ApiResponse<T>
- •Data structures:LinkedList<T>, Stack<T>
Generic Constraints:
Sometimes you want to limit what types can be used:
- •extends keyword:<T extends HasLength>
- •keyof operator:<K extends keyof T>
- •Multiple constraints:<T extends A & B>
Why Generics are Powerful:
- •Write once, use everywhere
- •Keep full type safety
- •Better than using 'any'
- •Makes code more flexible
- •Enables powerful utility types
Example
// Generic functionfunction identity<T>(arg: T): T { return arg;}// Usagelet stringResult = identity<string>("Hello");let numberResult = identity<number>(42);let booleanResult = identity<boolean>(true);// Generic interfaceinterface Container<T> { value: T; getValue(): T;}class Box<T> implements Container<T> { constructor(public value: T) {} getValue(): T { return this.value; }}const stringBox = new Box<string>("Hello World");const numberBox = new Box<number>(123);console.log(stringBox.getValue()); // "Hello World"console.log(numberBox.getValue()); // 123