🚀 Why Use Generics in TypeScript?
TypeScript generics allow you to write flexible, reusable, and type-safe code by enabling parameterized types.
❌ Without generics: Functions and classes require explicit types or rely on any
, which loses type safety.
❌ With any
: You lose type inference and risk runtime errors.
💡 Solution? Generics provide compile-time type safety while keeping the code flexible for different types.
📌 In this article, you’ll learn:
✅ How generics improve code reusability.
✅ How to use generic functions, classes, and interfaces.
✅ Best practices for applying generics effectively.
🔍 The Problem: Hardcoded Types Reduce Reusability
❌ Function Without Generics (Type-Specific)
function getFirstStringElement(arr: string[]): string {
return arr[0];
}
console.log(getFirstStringElement(["apple", "banana"])); // ✅ Works
console.log(getFirstStringElement([1, 2, 3])); // ❌ Error: Argument of type 'number[]' is not assignable to parameter of type 'string[]'
📌 Problems:
❌ Only works with string[]
– Requires separate functions for number[]
, boolean[]
, etc.
❌ Not reusable – You must write duplicate code for different types.
🚀 Solution? Use Generics!
✅ Solution: Using Generics for Flexibility
✔ Generic Function to Accept Any Type
function getFirstElement<T>(arr: T[]): T {
return arr[0];
}
console.log(getFirstElement(["apple", "banana"])); // ✅ "apple"
console.log(getFirstElement([1, 2, 3])); // ✅ 1
console.log(getFirstElement([true, false, true])); // ✅ true
📌 Why is this better?
✅ Reusable for any type — T
is dynamically inferred at compile time.
✅ Type-safe – Prevents passing invalid types.
✅ Eliminates duplicate functions – Works for string[]
, number[]
, boolean[]
, etc.
🚀 Use generics to make functions work with multiple types while maintaining type safety!
1️⃣ Generic Functions: Making Code More Reusable
✔ Using Generics with Multiple Parameters
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
console.log(pair("John", 25)); // ✅ ["John", 25]
console.log(pair(true, { name: "Amit" })); // ✅ [true, { name: "Amit" }]
📌 Why use multiple generic types?
✅ Works with mixed types (string, number
, boolean, object
, etc.).
✅ Flexible and type-safe at the same time.
🚀 Use <T, U>
for functions handling multiple types!
2️⃣ Generic Interfaces: Creating Flexible Object Structures
✔ Defining a Generic Interface
interface Box<T> {
content: T;
}
let stringBox: Box<string> = { content: "Hello" }; // ✅ Works with string
let numberBox: Box<number> = { content: 42 }; // ✅ Works with number
📌 Why use generic interfaces?
✅ Ensures consistent structure while supporting different types.
✅ Provides better type inference for objects.
🚀 Use generic interfaces to create flexible and reusable object structures!
3️⃣ Generic Classes: Type-Safe Object-Oriented Programming
✔ Creating a Generic Class
class Storage<T> {
private data: T[] = [];
add(item: T) {
this.data.push(item);
}
getAll(): T[] {
return this.data;
}
}
let stringStorage = new Storage<string>();
stringStorage.add("Apple");
stringStorage.add("Banana");
console.log(stringStorage.getAll()); // ✅ ["Apple", "Banana"]
let numberStorage = new Storage<number>();
numberStorage.add(100);
numberStorage.add(200);
console.log(numberStorage.getAll()); // ✅ [100, 200]
📌 Why use generic classes?
✅ Reusable for different types — No need for multiple implementations.
✅ Type-safe — Prevents adding wrong data types.
🚀 Use generic classes to manage collections and storage efficiently!
4️⃣ Generic Constraints: Restricting Accepted Types
✔ Limiting Generics to Specific Types
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(item: T): void {
console.log("Length:", item.length);
}
logLength("Hello"); // ✅ Works (string has a length)
logLength([1, 2, 3]); // ✅ Works (array has a length)
logLength(42); // ❌ Error: 'number' does not have a 'length' property
📌 Why use constraints?
✅ Prevents invalid type assignments.
✅ Ensures generics meet required structure.
🚀 Use <T extends Type>
to add constraints on generic types!
5️⃣ Generic Utility Types: Built-in TypeScript Features
✔ Using Partial<T>
to Make All Properties Optional
interface User {
id: number;
name: string;
email: string;
}
function updateUser(id: number, updates: Partial<User>) {
console.log(`Updating user ${id} with`, updates);
}
updateUser(1, { name: "Amit" }); // ✅ Only updates 'name'
updateUser(2, { email: "amit@example.com" }); // ✅ Only updates 'email'
📌 Why use Partial<T>
?
✅ Allows updating objects without requiring all fields.
✔ Using Readonly<T>
to Prevent Modifications
interface Product {
id: number;
name: string;
}
const product: Readonly<Product> = { id: 1, name: "Laptop" };
product.name = "Tablet"; // ❌ Error: Cannot assign to 'name' because it is a read-only property.
📌 Why use Readonly<T>
?
✅ Prevents accidental modifications.
🚀 Use Partial<T>
and Readonly<T>
for safer object handling!
🔥 Best Practices for Using Generics in TypeScript

🔑 Key Takeaways
✅ Generics allow you to write reusable, type-safe code.
✅ Use <T>
to create generic functions, interfaces, and classes.
✅ Use constraints (extends
) to restrict generic types.
✅ Use built-in TypeScript utility types like Partial<T>
and Readonly<T>
.
✅ Avoid any
– Use generics instead for better type inference.
By mastering TypeScript generics, your code will be more reusable, scalable, and safer! 🚀
Comments
Post a Comment
Leave Comment