TypeScript generics provide a powerful way to create reusable, type-safe components and functions that work with multiple types. They improve flexibility, reduce redundancy, and ensure strict type constraints without sacrificing reusability.
1. Generics Make Functions Flexible
Generics allow you to create functions that work with any type while keeping your code type safe.
function identity<T>(value: T): T {
return value;
}
const result = identity("Hello"); // result is of type string
2. Tuples Keep Their Order with Generics
Generics ensure that tuples maintain their exact structure and order, unlike regular arrays.
type Swap<T extends [any, any]> = [T[1], T[0]];
type Swapped = Swap<[string, number]>;
3. Generics Can Have Multiple Rules
Generics can enforce multiple constraints, making them even more powerful and flexible.
function merge<T extends object, U extends object>(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}
const merged = merge({ name: "Alice" }, { age: 25 }); // { name: "Alice", age: 25 }
4. Transform Objects Dynamically with Generics
Generics can dynamically change object structures using mapped types.
type ReadOnly<T> = { readonly [K in keyof T]: T[K] };
type User = { name: string; age: number };
type ReadOnlyUser = ReadOnly<User>;
5. Generics Can Make Decisions with Conditional Types
Generics can use conditional logic to decide types based on conditions.
type IsString<T> = T extends string ? "Yes" : "No";
type Result1 = IsString<string>; // "Yes"
type Result2 = IsString<number>; // "No"
6. Create Reusable Classes with Generics
Generics make classes adaptable to different data types, reducing code duplication.
class Box<T> {
constructor(private item: T) {}
getItem(): T { return this.item; }
}
const stringBox = new Box("Hello"); // Box<string>
const numberBox = new Box(42); // Box<number>
7. TypeScript Guesses Types for You
TypeScript can automatically figure out generic types based on how you use them.
function wrap<T>(value: T) {
return [value];
}
const numbers = wrap(10); // number[]
const words = wrap("hello"); // string[]
8. Generics Can Be Recursive
Generics can handle nested structures, making them perfect for complex data.
type NestedArray<T> = T | NestedArray<T>[];
const numbers: NestedArray<number> = [1, [2, [3, [4]]]; // Nested array of numbers
9. Simplify Function Overloads with Generics
Generics can replace multiple function overloads with a single, dynamic solution.
function identity<T>(value: T): T {
return value;
}
const result1 = identity("Hello"); // string
const result2 = identity(42); // number
10. Generics Can Work with Length
Generics can enforce rules based on properties like length, making them versatile.
function longest<T extends { length: number }>(a: T, b: T): T {
return a.length > b.length ? a : b;
}
const longerString = longest("apple", "banana"); // "banana"
const longerArray = longest([1, 2], [3, 4, 5]); // [3, 4, 5]