Some views on generics functions | Typescript

I started learning Typescript at the beginning of 2020 and since I came from Javascript, I became quite comfortable with my skills in this language once I learned how to create interfaces because suddenly I didn’t need to use any as a type of variables that would receive object literals. I knew Typescript was more powerful than that, and finally, I recently decided to learn more about its features and apply it daily.

I decided to start with Generics.

What I do understand about Generics

I see generics as a flexible mechanism in Typescript that enables developers to create reusable components in the form of functions, classes, interfaces, and types. In this blog post, I will focus exclusively on generic functions.

A trivial example

function log(content: any): any {
    return content;
}

log(3);
log("Hello hello");
log({ title: "Generics" });

This is a classic example, a function that receives anything and should return something.

As you can see in the code above, neither the developer nor the application has control over the data type that log() is expecting. But, with generics, it’s possible to improve the typing situation of the function above by making just a few changes:

function log<T>(content: T): T {
    return content;
}

log<number>(5);
// 5
log<string>(5);
// Argument of type '5' is not assignable to parameter of type 'string'.
log<string>("Log something about generics");
// Log something about generics

Example #1

Let’s suppose we have a function to sort an array of objects by prices in ascending order, before learning about generics I would most likely write it using the any data type:

function sortASC(items: any[]): any[] {
    return [...items].sort((itemA: any, itemB: any) => {
        return a.price - b.price;
    });
}

With this approach, I wouldn’t have too much control over the argument data type, and I would lose Typescript’s type checking capabilities. With generics, however, it’s possible to tackle that and make sortASC safer:

function sortASC<T extends { price: number; }>(items: T[]): T[] {
    return [...items].sort((itemA: T, itemB: T) => {
        return a.price - b.price;
    });
}

By extending T from an interface like I am doing in the code above, I am defining a constraint on T, meaning that whatever the interface passed as type, it should obey the interface which T extends it’s the initial definition.

Using generics, I am now able to set type when invoking the function. Here is a quick example calling sortASC using two different interfaces that follow the constraint { price: number; }:

interface Vehicle { price: number; cilyinders?: number; }
interface Bike { price: number; strokes?: number; }

// Sort an array of vehicles
sortASC<Vehicle>([{ price: 140000 }, { price: 21000 }]);

// Sort an array of bikes
sortASC<Bike>([{ price: 15000 }, { price: 12000 }]);

Example 2

For the second example, I will create a function that merges/concatenates two arrays. This function should receive two arrays as arguments, and it should return a single array with the items of both arrays:

function arrayMerger(arr1: any, arr2: any): any[] {
  return [...arr1, ...arr2];
}

Now, here’s how I rewrite it using generics with two types:

function arrayMerger<T, U>(arr1: T[], arr2: U[]): (T | U)[]{
  return [...arr1, ...arr2];
}

As you can see, the approach is the same as in the first example, but now with the particularity of having to types T and U and now, arrayMerger expects to return the union type (T | U)[] . Here is an example of how I call arrayMerger as a generic function strictly passing the types of each array argument:

arrayMerger<number, string>(
  [2001, 2002, 2005],
  ['Generics', 'Typescript']
);
// [2001, 2002, 2005], ['Generics', 'Typescript']

My last thoughts

From my own experience, investing time learning the different ways to apply generics on functions, classes, interfaces, and types is helping me write better Typescript code, and the more I learn about it, the more I know where and how to replace the dirty anys on TS code.

References

Please read the references I used on my personal studies:

Cover by

Firmino Changani

I'm Firmino, and here I write about web application development. Leave your comment below and let's discuss about the topic above. Cheers!