Generics in TypeScript allow us to define functions, classes, or components that accept a type as part of their definition.
In this way, they can behave differently based on the received type, adapting their generic behavior to different types.
Definition of Generic Functions
Generics are defined using the symbol < >
, and can be applied to both variables and functions.
For example, we can create a generic function that accepts any type of data:
function printValue<T>(value: T): void {
console.log(value);
}
In this example,
<T>
is the generic type that can be used to represent any type of data.- The function
printValue
accepts a parametervalue
of typeT
and simply prints it to the console.
It is very common to use “T” (for “Type”) to denote the type received by the function. But it is possible to use any other letter or name. :::
Creating a Generic Class
In addition to functions, we can also create generic classes in TypeScript. This allows us to create classes that can work with different types of data.
class Collection<T> {
private elements: T[] = [];
add(element: T): void {
this.elements.push(element);
}
get(index: number): T {
return this.elements[index];
}
}
In this example,
- We have created a generic class called
Collection
that can store any type of data. - The class has a method
add
that accepts an element of typeT
and adds it to an internal list. - It also has a method
get
that returns the element at a specific index.
Type Constraints in Generics
Sometimes, we may want to restrict the types that can be used with a generic (for example, if we want to ensure that only numeric types can be used with a generic function)
We can achieve this using type constraints in generics. For example:
function addNumbers<T extends number>(a: T, b: T): number {
return a + b;
}
In this example,
- We used the keyword
extends
to indicate that the generic typeT
must be a subtype ofnumber
. - This allows us to use the
addNumbers
function with any numeric type, but not with other data types.
Type Inference in Generics
TypeScript has the ability to automatically infer the generic type based on the provided data type (meaning that we do not have to specify it ourselves “manually”)
This allows us to write cleaner and more concise code without having to explicitly specify the generic type in every call. For example, consider the following generic function:
function printValue<T>(value: T): void {
console.log(value);
}
If we call this function without specifying the generic type:
printValue(42);
TypeScript will automatically infer that the generic type is number
, since we passed a number as an argument.