2

What Are Generics in TypeScript?

 1 year ago
source link: https://blog.bitsrc.io/what-are-generics-in-typescript-learn-this-for-better-typescripting-22bed7529f89
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

What Are Generics in TypeScript?

1*8qALT4gTaQL5qINfEy4c-A.jpeg

TypeScript, a “superset of JS”, makes it easier to build maintainable, comprehensible, and scalable apps thanks to the great power of type-checking.

Generics play an important role in TypeScript as it allows us to write reusable code that accepts both values and types as arguments.

Generics in functions

Generics help us in making our code more reusable. Let’s try to understand what are generics and why do we need them with the help of the below example

Please note in the above snippet, we have three functions doing pretty much the same thing. Its a duplicated code, and screams loud for making it reusable.

One thing we could do is to put a type of any so that String, Number, and Person type of values can call be used as arguments in the same function. Unfortunately, that introduces more problems then it solves (in general, if you’re planning to use the any type very often, it’s maybe best to keep it pure JS).

Solving the “reusability problem” using Generics — An example:

The converToValueArray function receives a value of the chosen type <T> and returns an array of that type: Array<T> . So, for example, if the value is of String type then the return type is Array<String>

Let’s see how TypeScript shows an error, when we define our generic type.

1*KlCd0lkRa34OeleTibyc9w.png

Please note, in line 18, after using the Generics, if we want to access age, it shows the correct error, which is what we want to get early feedback on any kind of error.

Inferred type

Let’s define a function that takes generic type.

function convertToArray<T>(args: T): Array<T> {
return [args];
}

We can call the function in two ways

convertToArray("someString");convertToArray<String>("someString");

As we can see, if the type is not passed in <> then the type is automatically inferred. Type inference keeps the code shorter, but in complex definitions, we might need to explicitly pass the type.

More than one generic type

Just like function arguments we can pass more than one type, which is irrespective of the number of function arguments. E.g

function doStuff<T, U>(name: T): T {
// ...some process
return name;
}

The above function can be called as follows:

doStuff<String, Number>("someString");

Generic classes

Many times we would require to create a generic class, e.g Base abstract classes. Where we can pass the types while creating instances of classes.

As we can see, in line 6 we have created a base local database class, which we can use to create an instance for one specific table and perform operations on the database instance. Let’s write a contact class which extends this base class so that it can inherit some properties from parent.

  • Line 14, when we extend the generic class, we have to pass two types, in this database. In our case ContactTable and ContactModel.
  • Line 17: The ContactLocalDatabase will get the functions from the parent class, and has to override the getFormattedData, as this is defined as an abstract function in the parent base class.
  • Line 17: This is now the function that has a generic type, which we discussed in the first section.

Let’s create an instance of the ContactLocalDataBase to see class generics in action.

  • Line 1: as we have defined the types of ContactLocalDatabase class when using new keyword, the types need not be passed to the base class.
  • Line 3, 11, 13: we can notice that these functions are from the abstract class. These behave as per the generic class definitions.

Generic constraints

So far, it is quite clear that generics are a way of writing code such that our code can support more than one type and the type can be passed as a parameter.

Given this knowledge, the passed type can be any predefined type or complex user-defined type.

Sometimes if we want to access any function from the generic typed variable, it will throw an error.

1*zPCYDkGeLmXgXZyN_JdhDg.png
  • Line 6: This is totally valid error, as data has a generic type and it can be either String, Number, Float, or any other type and the passed data may or may not have length.

Hence we can add some constraints to any generics, where TS will make sure, only those values can be passed to function or class, which fulfills the constraint. Let’s add some constraints to our generic function definition.

1*S6K2b6RJZP_Gzp6FcUOAnA.png
  • Line 4: we have extended the type T to have a length property
  • Line 5: the error disappeared, which said property length does not exist on type ‘T’
  • Line 10: when we call the function with a number, it throws error explaining it doesn’t fulfill the constraint
  • Line 12 and 13: when we pass valid data like String or Array, TS doesn’t throw an error.

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK