- Home
- Typescript
- Generics
Generic Types
Generics in TypeScript are a powerful way to create flexible, reusable code structures. They allow us to define types, functions, and other structures that can operate with different data types while maintaining strict type safety. In simpler terms, generics enable us to design components that work uniformly across a variety of types without sacrificing TypeScript's robust type-checking features.
One of the most common examples of generics is Array<T>
. Here, T acts as a placeholder for any type. For example, Array<string>
represents an array of strings. The T can be swapped with any other type depending on the context.
Defining a Generic Type
We can create custom generic types that allow us to define relationships between different object properties. Let's look at an example:
type Group<T> = {
leader: T;
members: T[];
};
In this example, T
represents a placeholder type, and it can be replaced by any specific type when the generic type is used. For instance, if we set T
to string
, Group<string>
would be a structure where the leader and members are all strings:
let stringGroup: Group<string> = {
leader: 'Alice',
members: ['Bob', 'Charlie', 'Dana']
};
Similarly, we could use number
in place of T
:
let numberGroup: Group<number> = {
leader: 1,
members: [2, 3, 4]
};
The Flexibility of Generics
Generics allow for great flexibility because they adapt to the type specified when creating an instance. You can use generics not only with objects but also with functions. For instance, a function that reverses an array can benefit from generics like this:
function reverseArray<T>(items: T[]): T[] {
return items.reverse();
}
let reversedStrings = reverseArray(['apple', 'banana', 'cherry']);
let reversedNumbers = reverseArray([1, 2, 3]);
In this example, the reverseArray
function is capable of handling both an array of strings and an array of numbers, all while maintaining type safety.
Why Use Generics? Generics are especially useful when we want to create reusable components. By allowing a function, type, or class to accept any type as a parameter, we create flexible solutions that don't rely on hard-coded data types. Generics also help to:
- Ensure consistency: All generic parameters must be of the same type.
- Reduce duplication: Instead of writing separate functions or types for each data type, you create one generic structure.
- Enhance readability: Generic types make code more self-documenting, helping developers to better understand the intent of the code.
In summary, generics in TypeScript provide a powerful mechanism for writing reusable and flexible code that works across various types. By incorporating generics into your TypeScript projects, you can create cleaner and more maintainable code.
Generic functions
Generic functions in TypeScript allow us to write flexible, reusable functions that can handle different data types while maintaining type safety. These functions are particularly useful when you want to work with varying types but ensure consistency in how they are handled.
Example of a Generic Function
Imagine we want to write a function that creates an array filled with a specific value repeated a certain number of times. In JavaScript, this could look something like:
function fillArray(value, count) {
return Array(count).fill(value);
}
For example, calling fillArray('apple', 3)
would return ['apple', 'apple', 'apple']
. This works fine, but there's no type safety — we don't know what type the value
should be, and we can accidentally mix types without TypeScript warning us.
Adding TypeScript Generics
With generics, we can create a type-safe version of the fillArray function. Here's how:
function fillArray<T>(value: T, count: number): T[] {
return Array(count).fill(value);
}
In this version, the function accepts a type parameter T
, which is used to ensure that the type of value
is consistent throughout the function. The array returned by the function will contain elements of type T
. Now, TypeScript enforces that the types of value
and the elements of the resulting array must match.
Using the Generic Function
When calling the function, TypeScript automatically infers the type based on the passed argument:
const fruits = fillArray('banana', 4); // ['banana', 'banana', 'banana', 'banana']
const numbers = fillArray(7, 3); // [7, 7, 7]
In these examples, fillArray('banana', 4)
returns an array of strings, while fillArray(7, 3)
returns an array of numbers. TypeScript ensures that the types are correct based on the first argument provided to the function.
If necessary, you can also explicitly specify the type when calling the function:
const veggies = fillArray<string>('carrot', 2); // ['carrot', 'carrot']
However, in most cases, TypeScript's type inference is smart enough to figure out the type without you needing to specify it.
Benefits of Generic Functions
Generic functions give us several advantages:
Reusability: You don't need to write separate functions for different types. One generic function can handle multiple types. Type Safety: TypeScript ensures that the data types are consistent, reducing the risk of errors in your code. Flexibility: Generics allow the same function to work with various types, making your code more adaptable.
Generic Functions with Multiple Type Parameters
Sometimes, you may need to work with multiple types in a single function. In such cases, you can introduce multiple type parameters:
function pairValues<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
const mixedPair = pairValues('hello', 42); // ['hello', 42]
Here, the function pairValues
accepts two parameters of potentially different types, T
and U
, and returns them as a tuple. You can use any combination of types when calling the function.
Conclusion
Generic functions in TypeScript provide a powerful tool for creating flexible, reusable code that works with various types. By using generics, you maintain type safety while avoiding repetitive code, making your TypeScript projects more robust and easier to maintain.