TypeScript Generics with Examples
TypeScript generics are a powerful feature that allow you to create reusable and type-safe components. Generics provide a way to create classes, functions, and interfaces that work with a variety of types while maintaining strong type safety. This article will introduce you to generics and demonstrate how to use them with practical examples.
Understanding Generics
Generics enable you to define a component with a placeholder for the type it operates on. Instead of specifying a concrete type, you use a generic type parameter that can be replaced with any type when the component is used.
Basic Syntax
The basic syntax for defining a generic type is to use angle brackets <>
with a type parameter name. Here’s a simple example:
function identity(value: T): T {
return value;
}
const stringIdentity = identity("Hello"); // string
const numberIdentity = identity(123); // number
In this example, identity
is a generic function that takes a parameter value
of type T
and returns a value of the same type. The type parameter T
is replaced with the actual type when the function is called.
Generics with Classes
Generics can also be used with classes to create flexible and reusable data structures. Here’s an example of a generic class:
class Box {
private value: T;
constructor(value: T) {
this.value = value;
}
getValue(): T {
return this.value;
}
}
const stringBox = new Box("TypeScript");
console.log(stringBox.getValue()); // Output: TypeScript
const numberBox = new Box(42);
console.log(numberBox.getValue()); // Output: 42
In this example, the Box
class is defined with a generic type parameter T
. The class has a private property value
of type T
and a method getValue
that returns the value of type T
.
Generics with Interfaces
Generics can be used with interfaces to create flexible and type-safe interfaces. Here’s an example:
interface Pair<T, U> {
first: T;
second: U;
}
const pair: Pair<string, number> = {
first: "Age",
second: 30
};
console.log(pair.first); // Output: Age
console.log(pair.second); // Output: 30
In this example, the Pair
interface is defined with two generic type parameters T
and U
. The interface represents a pair of values with types T
and U
, respectively.
Generics in Functions
Generics can be used in functions to handle multiple types while maintaining type safety. Here’s an example of a generic function that works with arrays:
function reverseArray(items: T[]): T[] {
return items.reverse();
}
const reversedStringArray = reverseArray(["one", "two", "three"]);
console.log(reversedStringArray); // Output: ["three", "two", "one"]
const reversedNumberArray = reverseArray([1, 2, 3]);
console.log(reversedNumberArray); // Output: [3, 2, 1]
In this example, the reverseArray
function takes an array of type T
and returns a reversed array of the same type. The type parameter T
ensures that the function works with arrays of any type while maintaining type safety.
Constraints on Generics
Sometimes you may need to impose constraints on the generic type parameter to ensure it has certain properties. This is done using constraints:
function logLength(item: T): void {
console.log(item.length);
}
logLength("Hello, TypeScript"); // Output: 16
logLength([1, 2, 3]); // Output: 3
// logLength(123); // Error: number does not have a length property
In this example, the logLength
function is constrained to types that have a length
property. This allows the function to accept strings and arrays but not numbers or other types without a length
property.
Conclusion
Generics in TypeScript provide a powerful way to create flexible and reusable components while maintaining strong type safety. By understanding and utilizing generics, you can write more generic and adaptable code, improving the overall quality and maintainability of your TypeScript applications.
Experiment with generics in your projects to see their benefits in action and enhance your TypeScript programming skills.