Generic Functions in TypeScript.

Author
By Darío Rivera
Posted On in TypeScript

It seems like there is nothing beyond functions and call and constructor signatures in TypeScript. However, this is where the concept of generic functions comes in. In this post, we will see everything related to this type of functions in TypeScript.

Let's start this article by looking at the following code:

interface Contact {
   id: number;
   name: string;
}

function clone(source: Contact): Contact {
   return Object.apply({}, source);
}

const x: Contact = { id: 1, name: 'Bob' };
const y: clone(x);

We have already seen the use of Interfaces in TypeScript before. The really interesting thing about this example is the clone function. This function takes a Contact type as a parameter and returns a clone of the same type.

If you look more closely, there is nothing specific in the cloning function that requires a Contact type to be passed. We could actually use clone to clone any type of object, the only thing that really matters to us is that the return value is of the same type as the received parameter.

That being said, we can turn the clone function into a generic function as follows:

function clone<T>(source: T): T {
   return Object.apply({}, source);
}

The first part after the function name clone<T> is the type identifier. It is generally used T but nothing prevents you from using any other identifier name. Once defined, it can be used in the rest of the signature (source: T): T. This way, our clone function can be used with any type and will always check that the parameter type is the same type of the return.

Let's now look at a more common example. Consider the following function that returns the first element of an array.

function firstElement(arr: any[]) {
  return arr[0];
}

This function takes an array of any type as a parameter and returns the first element. We have used the any type since the function can be used with arrays of any type. However, this brings us a problem, using any is like not using typing at all. Ideally, if we have an array of numbers the function should return a number, and so on with all types.

Let's turn this function into a generic function by adding the appropriate types.

function firstElement<T>(arr: T[]): T | undefined {
  return arr[0];
}

A union type has been used (see data types in TypeScript) to add the undefined value. With this, every time we use this function, TypeScript will check that the return value is of the same type as the elements passed in the array.

Constraints

Constraints serve to give a little less broad scope to the types we use in the signatures. Consider the following example that gets the object with the highest length of two objects passed as a parameter.

function max<T>(a: T, b: T) {
  if (a.length >= b.length) {
    return a;
  } else {
    return b;
  }
}

The problem with the previous code is that we cannot guarantee that the objects passed always have the length property. For this, TypeScript allows us to reduce the scope of these objects by adding a constraint on the type. See the same example with this constraint.

function max<T extends { length: number }>(a: T, b: T) {
  if (a.length >= b.length) {
    return a;
  } else {
    return b;
  }
}

Thus, although the type can be any object, it must have at least the length property. Let's see an example.

interface Box {
   width: number;
   length: number;
}

interface Figure {
    length: number;
}

const box: Box = { width: 10, length: 20 };
const fig: Figure = { length: 5 };

// returns Box object
max(box, fig);

Note that although the two objects passed as parameters have a different interface, both have the length property. This works since TypeScript has a Structural Type System. Needless to say, it can also be extended from other previously defined types.

interface Lengthable {
   length: number;
}

function max<T extends Lengthable>(a: T, b: T) {
   ...
}

Acerca de Darío Rivera

Author

Application Architect at Elentra Corp . Quality developer and passionate learner with 10+ years of experience in web technologies. Creator of EasyHttp , an standard way to consume HTTP Clients.

LinkedIn Twitter Instagram

Sólo aquellos que han alcanzado el éxito saben que siempre estuvo a un paso del momento en que pensaron renunciar.