Generics in TypeScript

What are generics in TypeScript, commonly known as generics, a TypeScript utility closely related to its typing system.

In this article we are going to cover the concept of generics in the TypeScript language and see some simple examples. It is something that does not exist in Javascript and therefore is available to us only when we develop in TypeScript. Generics are a utility that we have in the language at development or compilation time, like interfaces. That is to say, once the code has been compiled, already in Javascript, there is no such construction and therefore they will not be able to help us at execution time.

Generics are constructs already available in languages ​​like C# or Java, so you probably already know what we’re talking about. Whether you have an idea or not, we hope to help you by clarifying the concept, to see some code to clarify its syntax and usage.

Note: Remember that you have more on TypeScript, clarifying more basic things in the .

What are generics

We can understand generics as a kind of code “template”, through which we can apply a certain data type to various points in our code. They serve to take advantage of code, without having to duplicate it due to type changes and avoiding the need to use the “any” type.

Thus said the previous affirmations, perhaps they do not tell you much. Instead, the easiest way to understand generics is through an example.

Imagine you have a function that displays a numeric value and then returns it. Later they ask you to do that same functionality, but with a string. To achieve this we have two alternatives:

a.- Create two functions with different type declarations

function display(value: number): number { console.log(value); return value; } function displayString(value: string): string { console.log(value); return value; }

The problem with this alternative is obvious, since we have practically repeated the same code twice. Even if overloading were applied to a class, the code would be pretty much the same.

b.- Use a single function in which we receive and return the type any

function display(value: any): any { console.log(value); return value; }

Now the problem is that “any” doesn’t help us at all in TypeScript. It doesn’t warn us if we make a mistake by assigning other data types and the editor stops helping us with intellisense.

The solution is to apply the generics

Through generics we can indicate that what is received has a malleable type. That is, it can support various types, but nevertheless, within the function we can refer to the type that is being supported, so that the compiler and editor can help us where they can.

TypeScript Generic Syntax

Generics are enclosed in “less than and greater than” as if they were HTML tags, assigning an alias to the variable type being received. We can then use that alias to define the type.

function display(value: theType): theType { console.log(value); return value; }

Here we are defining the generic “theType”. This allows us to indicate the type of the parameter with the alias, as well as the type of the return value. In practice it will make the type of the return value deducible from the type of the received value.

Thanks to this situation we managed to get rid of the “any” type declaration, which didn’t help at all, and replace it with a generic. Although it is true that they do not help the same as if it were a type written on fire, they do represent some interesting help as we can see in the following image:

As you can see, here the editor is able to infer the type of the “data” variable, since what the display() function returns is of the same type as the value it received.

For example, here the editor shows us an error for trying to assign data of another type to a variable.

A generic can be any type, including a class or an array.

When we use a generic we can deliver any type, even an object-oriented programming class.

class Animal { } function care(something: T): T { return something; } let something = care(new Animal());

The variable “something” in the last line of code would be inferred to be of type Animal, since the parameter sent was an object of that class.

There are various utilities in generics that help us define the type with a narrower range.

Another interesting example is the following, in which we define that the parameter will be an array of elements of the generic type.

function display(value: T): T { console.log(value.length); return value; }

Since the parameter has been declared as an array of “T” elements, it is certain that within our function we can access the “length” property of that parameter.

There is no Javascript translation of a generic

Generics have no translation to Javascript code, since Javascript does not support types. As we said, generics only help you at development time.

To observe this detail, it is interesting to see the translation that the TypeScript compiler makes of a function where generics are used. For example:

function display(value: T): T { console.log(value); return value; }

It is translated to Javascript by this other code:

function display(value) { console.log(value); return value; }

Two generics in one function header

In the head of a function we can also define two generic types, separated by commas, as can be seen in the following code.

function test(obj: T, key: K) { return obj; }

Here we have indicated that we will have two parameters. The first is generic and could be of any type, while the second must be a value found as the key of the first parameter. Therefore, the first parameter would have to be an object and the second parameter one of the strings that serve as keys within that object.

let professional = { name: ‘Miguel A A’, company: ‘.com’ } console.log(test(professional, ‘company’)); // Perfect call console.log(test(professional, ‘nonexistentproperty’)); // incorrect call, because the property does not belong to the object

The last line will give a compilation error, since the “nonexistentproperty” does not exist as a property of the object received as the first parameter.

let professional = { name: ‘Miguel A A’, company: ‘.com’ } console.log(test(professional, ‘company’)); // Perfect call console.log(test(professional, ‘nonexistentproperty’)); // incorrect call, because the property does not belong to the object

generic classes

The set of resources available with generics increases when we use them with classes. So we can have a class in which we declare the use of a generic type.

class Generic { doSomething(x: T, y: T): T { return x; } }

As you can see, that generic can be used throughout the code of the class, for example, the doSomething() method receives two generics and then returns another generic.

When instantiating an object of this class, we can indicate the specific type of data that will be applied to that generic.

let instance = new Generic();

Now, using the methods of the class, it will be possible to infer the type of data received by the methods with generic parameters or the returned generic data type.

let variable = instance.doSomething(3, 4);

Here, “variable” will be deduced as type “number”, as you can see in the following image.

The only problem with this is that in the doSomething() method, as defined there, it is impossible to know what type of parameters are in the doSomething() method implementation, so trying to do operations with them can lead to compile-time errors.

So you can take an alternative of declaring the methods with their generic types and do the implementation later. It is more or less what you can see in the following code.

class Generic { sum: (x: T, y: T) => T; } let instance = new Generic(); instance.sum = function(x, y) { return x + y; } let instanceString = new Generic(); string instance.sum = function(x, y) { return x + y; }

We could then use these methods to produce output, passing parameters of the appropriate types, otherwise the compiler will complain.

let variable = instance.sum(3, 4); console.log(variable); let variableString = instanceString.sum(“Hello”, “.com”); console.log(variableString);

Note: Perhaps a clearer code would make the Generic class abstract, because then you can have the sum method declared as abstract. Then make derived classes that implement that abstract method, to be able to instantiate objects, already indicating the concrete types. Perhaps the problem with abstracts can come to you because you couldn’t instantiate objects if necessary, while in this code using generics you could.

Add restrictions to generics

The problem that we have begun to observe in the previous example can be solved by adding a restriction to generics.

Let’s look at this code:

function show(data: T) { console.log(data.length); }

If my generic received as a parameter were a string, or an array, I could be sure that its length property can be accessed in the body of the function. However, the generic can accept anything, so the compiler complains that anything won’t necessarily have the length property.

remember that the generic is not an “any” type. If the parameter were declared as “any” the compiler would not complain. The difference is that the generic, although technically it can be anything, the compiler must worry that everything it receives, whatever it is, can perform the operations that are marked in the implementation of the function.

An alternative to solve it is to restrict the generic by means of an interface. Then we can say in the function that the generic can be anything that takes that interface.

interface withLength { length: number; } function show(data: T) { console.log(data.length); } Now if I try to do something like this: show(3);

The compiler will complain that the “number” 3 is not something that has the length property. ” Argument of type ‘3’ is not assignable to parameter of type ‘conLength’.”

However, if I pass it a string, there will be no problem.

display(“test”);

We can also pass any object, as long as it has the length property:

show({ length: 3, something else: ‘test’ });

However, if the object has the length property but is not of type number, the compiler will throw an error.

to show({…

See also  Cosmos
Loading Facebook Comments ...
Loading Disqus Comments ...