Learn about Generic types in TypeScript

Tram Ho

In this article, we will go into depth about Generics (Generic data types) in Typescript. For you C # or Java devs, Generic data type is extremely familiar, but for you javascript devs when you first switch to Typescpirt, it will be a bit difficult to approach this concept.

1. Introduction

First, let’s find out what a Generic type is?
As per TypeScript’s definition of Generic:

In languages ​​like C # and Java, one of the main tools in the toolbox for creating reusable components is generics, that is, being able to create a component that can work over a variety of types rather than a single one. This allows users to consume these components and use their own types.

Simply put, a Generic type is the permission to pass the type to components (function, class, interface) as a parameter. This will make the components more flexible. Better reuse.

2. Why need Generic

We create a function that takes 2 parameters of the same data type (string | number) and returns a Turple .

In the above example the function getTuple has 2 parameters a and b type NS ( Union type ) and returns a tuple [NS, NS] .
Now we have a few problems with the above function:

  • First, we cannot bind a and b have the same data type because a and b can be either string or number.
  • Second is when fuction returns a tuple (array) containing values ​​of type string or number and Typescript compiler does not allow us to do so because it needs to know the exact data type of the return values. .

The way to solve the problem is to use any type for a and b and tuple [any, any]. Or we can use type assertion to cast a value in tuple ( NS to string or number ).
However, both methods can cause errors if we do not manually check the data types of the values.

And Generic type appears, helping us to solve the above problems

Typescript has strong support for generics, we can use generic for function, class, interface …
Now we will modify the above example using a Generic function:

We can bind a and b to the same data type using generic type.
When we use getTuple<number> syntax, the getTuple<number> compiler will replace T to number . So the TS compiler will interpret the getTuple function as below:

Thus the return value of the function is tuple [number, number] and the TS compiler will let us manipulate this tuple. (Similar to string )
We can replace T with any parameter. Syntax f<Type>() .

In the above example, we noticed, when we called getTuple( 1.25, 'world' ) In the function call, we removed the value of the generic parameter. So TypeScript will infer the data type for T since the type of the first argument1.25 is number . Since the second argument must be of the same type, this call will result in a compiler error because both arguments must be of the same type as each function declaration. Similarly if we change the first argument to ‘word’ then the TS compiler will deduce the data type of T is string .

We can rewrite the function getTuple with the generic arrow function as follows:

3. Declare Generic type

We can use generics for function, class, interface …

3.1 Generic Function

As we all know, TypeScript can deduce data types from value. If you hover your mouse over the getTuple function name in your IDE, you’ll see the below return data type of the function. This is the type of function we just created.

A generic type can contain many different parameters representing many different data types, for example if the two parameters a and b are different then we need 2 different parameters to represent their own data type. .

So when we call getTuple( 1.25, 'world' ) the compiler will stop error because the arguments a and b can have separate types.
The way to declare the getTuple function is similar:

3.2 Generic Interface

An Interface can also represent a function.

The above interface will represent a function that takes 2 arguments, number and string , and returns a value of type any We can use this interface to declare the type of the function:

Arguments a , b will accept the type number and string . The return value will be of type any .

3.2.1 Declare a generic function to use interface

This example is similar to the previous example, except we create an interface with a generic function. Will give us more flexibility with data types of a and b .

3.2.2 Define a generic interface

Interfaces allow us to define the properties and methods of an object. Imagine an object with properties a and b and getTuple function getTuple returns tuple with data types of a and b ?

You may think the above, but this will not be correct. ERROR : cannot find name 'T' . The correct syntax would be:

Ex:

Consider another scenario for the example above. What if the getTuple function accepts a generic argument?

Now the type of c is fixed according to V
In some cases we want the type of c be more flexible without depending on the generic TupleObject we can do:

3.3 Generic class

Ex:

In the above example, Collection is a generic class, when creating an instance of the Collection class with the keyword new we provide type T with syntax new Collection<Type> . The TS compiler will replace T with the type we provided.
Type T is used (inferred) in public , private , protected properties, in methods as well as contructor . However, for static property or method, TS compiler does not use generic T

You can convert the generic type from subclass to superclass in inheritance shown below. I have used the U parameter only to prove that the parameter name does not matter between classes and the same applies to Interface.

4. Constraint generic type

4.1 Constraint by extends

Ex:

The merge function is a function that merges 2 objects, Ex:

It works fine, the merge () function wants you to pass 2 objects, but it doesn’t prevent you from passing it like this:

TS doesn’t get any errors, merge () works with all data, but we can force merge function to work with objects. To do that, we use the extends keyword:

Now the merge () function has a data type constraint.

4.2 Constraint by extends keyof

Ex:

The props function takes 2 parameters, an object, and a key to the object. The compiler will get the following error:

To overcome this, you must bind data type K to be the key of data type T

If you pass prop () a valid key then the compiler will not issue:

Summary

  • Using generic types in typescript to create flexible, reusable funtions, interfaces, classes …
  • Use the extends keyword to limit a parameter’s data type to a specific data type.
  • Use the extends of keyword to bind the data type to another object’s property.

References

https://www.typescriptlang.org/docs/handbook/generics.html
https://www.tutorialsteacher.com/typescript/typescript-generic
https://medium.com/jspoint/typescript-generics-10e99078cc8

Share the news now

Source : Viblo