When creating applications, writing reusable and type-safe code is important. TypeScript offers Generics, which allow us to write code that works with multiple types while maintaining strong typing. This makes our code flexible and robust. In this blog, I’ll explain what generics are, why they’re useful, and how you can use them. 🚀
What Are Generics? 🧩
Generics in TypeScript let you create reusable components that can work with any data type. You don’t need to write multiple versions of the same code for different types. Instead, you can use a placeholder for the type and specify the actual type when using the code.
Why Use Generics? 🤔
- Reusability: Write one function or class and use it with any type. ♻️
- Type Safety: Catch errors during development, not at runtime. ✅
- Flexibility: Handle dynamic types without losing TypeScript’s benefits. 🌟
How Do Generics Work? ⚙️
Generics use type parameters as placeholders for types. These type parameters can be replaced with specific types when the function or class is used.
Here’s a simple example:
function identity<T>(value: T): T {
return value;
}
- The
<T>
is the generic type parameter. - You can replace
T
with any type, and the function will return the same type it receives.
Examples of Using Generics 📚
1. Generic Functions 💡
Imagine you need a function that returns an array of any type:
function getArray<T>(items: T[]): T[] {
return items;
}
// Usage:
const numbers = getArray<number>([1, 2, 3]); // number[]
const strings = getArray<string>(["a", "b", "c"]); // string[]
This function works for both numbers and strings! 🎉
2. Generic Classes 📦
Generics are also useful in classes. For example:
class Box<T> {
private contents: T;
constructor(value: T) {
this.contents = value;
}
getContents(): T {
return this.contents;
}
}
// Usage:
const numberBox = new Box<number>(42);
const stringBox = new Box<string>("Hello");
Here, the Box
class can store values of any type. 🛠️
3. Generic Interfaces 📝
You can define interfaces with generics to handle multiple types:
interface Pair<T, U> {
first: T;
second: U;
}
// Usage:
const pair: Pair<number, string> = { first: 1, second: "hello" };
4. Constraining Generics 🚧
Sometimes, you need to restrict what types a generic can accept:
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person = { name: "John", age: 30 };
const name = getProperty(person, "name"); // Works
// const invalid = getProperty(person, "height"); // Error
Here, the key
must be a property of the obj
. 🔒
5. Default Type Parameters ⚡
You can assign a default type to a generic:
function createList<T = string>(): T[] {
return [];
}
// Usage:
const defaultList = createList(); // T defaults to string
const numberList = createList<number>(); // T is number
Why Generics Are Useful 🌟
- Prevents Code Duplication: You don’t need separate functions or classes for every type. ✂️
- Ensures Type Consistency: Generics help you enforce relationships between types. 🔗
- Improves Code Readability: The use of explicit type parameters makes your code clearer. 👓
Best Practices 🏆
-
Use Descriptive Names: Instead of just
T
, use meaningful names likeItemType
orKeyType
. ✍️ - Rely on Inference: Let TypeScript infer the generic type whenever possible. 🔍
-
Add Constraints: Use
extends
to limit the acceptable types for a generic. 🚦
Conclusion 🎯
Generics are an essential feature of TypeScript that make your code reusable, flexible, and safe. By understanding how to use them, you can write better and cleaner code for any project. If you’re just starting with generics, try simple examples first and gradually move on to more advanced use cases.
Got an idea where generics could help in your project? Let me know in the comments! 💬
Top comments (0)