Introduction to Typescript Generics
By the end of this Article, you will learn about typescript generics, what they are, their use cases, and a few examples.
In some cases, the types we create can be reusable as other types in a different context, and we do not need to start duplicating for each use case, we can use a generic to avoid duplication. Generics are used to create reusable types, as parameters are to functions so are generics to types.
For example,
enum Subject = {
Maths = 'Maths',
English = 'English',
Deutsch = 'Deutsch'
}
type Book = {
subject: Subject,
author: string,
year: number
}
/**
this function is expected to receive math books only, both book can be
Maths, Deutsch or English
*/
function takeMathsBookOnly(book: Book) {
}
In the code snippet above, we have a type Book
with a property subject
of enum Subject
that can be Maths, English, or Deutsch, and we also have a function takeMathsBookOnly
that expects to receive math books only. However, books as we know can be of be of any subject.
We can write a type guard within the function or write some condition to determine the book subject is Maths, but all of this complex logic could be avoided by using generics, we can create a specific type for each subject by using generics.
enum Subject = {
Maths = 'Maths',
English = 'English',
Deutsch = 'Deutsch'
}
// Default parameter = Subject, meaning Book
type Book<T = Subject> = {
subject: T, // subject: Subject
author: string,
year: number
}
type MathBook = Book<Subject.Maths>
/** subject can only be equal to 'Maths'
{
subject: Subject.Maths,
author: string,
year: number
}
**/
type EnglishBook = Book<Subject.English>
/** subject can only be equal to 'English'
{
subject: Subject.English,
author: string,
year: number
}
**/
type DeutschBook = Book<Subject.Deutsch>
/** subject can only be equal to 'Deutsch'
{
subject: Subject.Deutsch,
author: string,
year: number
}
**/
function takeMathsBookOnly(book: MathBook) {
}
From the snippet above, Book
now accepts a generic type variable called T
, T
just like function parameters accept Subject
as it default type, so Book
type is the same as the previous functionality, however, the added advantage is that we can now reuse the Book
type in different contexts, we can create MathBook
type only by passing Subject.Maths
as an argument to Book
, so in the case T = Subject.Maths
. We can create as many specific types as we want using generics.
Another Generic Use case
// generic map function
const map = <T, U>(items: T[], fn: (item: T) => U): U[] => {
const result: U[] = [];
for (let item of items) {
result.push(fn(item));
}
return result;
}
const doubleNumbers = map<number, number>([1, 2, 3, 4, 5], (item) => item * 2);
// [2, 4, 6, 8, 10]
const stringifyDoubleNumbers = map<number, string>([1, 2, 3, 4, 5], (item) => `${item * 2}`);
// ['2', '4', '6', '8', '10']
Let’s examine another generic use case, in the above snippet we have a generic map
function with two generic type variable T
and U
, which accepts items
that is an array of T
, and a callback function fn
which accepts an item of the type T
and returns U
, sometimes U
could be the same as T
like in the doubleNumbers
, but that is not always the case, we might need to transform items to another type like in the stringifyDoubleNumbers
which receives an array of number
but returns an array of string
.
To read more on generics, check out Typescript Generics. Thank you