Mastering TypeScript Generics: Building Scalable and Reusable Components
Learn how to use TypeScript generics to create flexible, scalable, and reusable components that improve your code quality and maintainability.
TypeScript generics are a powerful feature that allow you to create components and functions which can work with a variety of data types instead of being limited to one. This flexibility helps you write reusable code components and improve scalability in your projects. In this tutorial, we'll explore the basics of generics and build practical examples to master this concept.
Generics work by defining a placeholder type parameter, usually with a letter like
function identity<T>(arg: T): T {
return arg;
}
const output1 = identity<string>("Hello Generics!"); // Output is string
const output2 = identity<number>(42); // Output is number
console.log(output1); // Hello Generics!
console.log(output2); // 42In the example above, function identity takes an argument of type T and returns the same type. When calling the function, you specify the type, and TypeScript enforces that the argument and return types match.
Next, let's create a generic interface for an array wrapper that ensures type safety but works with many types.
interface MyArray<T> {
items: T[];
add(item: T): void;
get(index: number): T;
}
class ArrayWrapper<T> implements MyArray<T> {
items: T[] = [];
add(item: T): void {
this.items.push(item);
}
get(index: number): T {
return this.items[index];
}
}
const numberArray = new ArrayWrapper<number>();
numberArray.add(10);
numberArray.add(20);
console.log(numberArray.get(1)); // 20
const stringArray = new ArrayWrapper<string>();
stringArray.add("hello");
stringArray.add("world");
console.log(stringArray.get(0)); // helloHere, the ArrayWrapper class uses generics to handle arrays of any type, but retains strict type checking. This means you can't accidentally add a string to a number array, reducing bugs.
Generics also shine when building reusable React components or utility functions. For instance, a generic function to merge two objects can be written as follows:
function mergeObjects<T extends object, U extends object>(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}
const merged = mergeObjects(
{ name: "Alice" },
{ age: 30 }
);
console.log(merged.name); // Alice
console.log(merged.age); // 30In this example, two objects of different types are merged and the resulting object has a combined type of both. Using extends object ensures we only accept objects and the return type reflects the merged type, enhancing type safety.
To summarize, here are key points to master TypeScript generics:
- Use generics to write flexible functions, interfaces, and classes.
- Define generic type parameters inside angle brackets: