Generic functions are functions that are capable of accepting different data types as arguments and are also capable of returning different data types as return values.

In this article, we will look at how to declare and implement generic functions in TypeScript.

Generic function

We will first look at how functions are declared in TypeScript. We will start by creating a simple function that takes one argument and returns the argument value.

function display(message: any) {
  return message;
}

We have declared a function display() which takes message parameter of type any and simply returns the message.

To call this function we can write,

display("Hello"); // Hello
display(1); // 1
display(true); // true

The return type of display() function is any. Let’s say we want to have more control over our function. Rather than using any we want to let TypeScript know the type of data we are passing to this function and also the type of value this function can return.

function display(message: string): string {
  return message;
}

display("Hello"); // Hello

This looks good. We are passing a string to the function and also specifying the return type of the function as a string.

But, what if we want to pass a number to this function?

display(1); // ❌ Argument of type 'number' is not assignable to parameter of type 'string'.

Well, TypeScript is going to complain, Argument of type 'number' is not assignable to parameter of type 'string'.

We did not encounter this error when the function was able to take a parameter of type any. But, any is obviously not the best approach here because it defeats the purpose of using TypeScript. Let’s see how defining the above function as a generic function can help us.

function display<T>(message: T): T {
  return message;
}

We declare display() as a generic function with angle brackets(<>) after its name. Within the angle brackets is a placeholder T for the data type. You can name it anything you want. For example,

function display<X>(message: X): X {
  return message;
}

The advantage of using such a generic function is you can call it with any data type, such as string, number, etc.

function display<T>(message: T): T {
  return message;
}

display < string > "Hello"; // Hello

In the above code, we are calling function display() by supplying string in the angle brackets <string>. This will replace the placeholder <T> with type string. Hence, the parameter(message: T) will also be of type string and the return value type is also of type string.

This gives us the flexibility to supply any type we want to the function.

display < number > 1; // 1
display < boolean > true; // true

Let’s look at another example,

function convertArray<T>(value: T): T[] {
  return [value];
}

convertArray < string > "Hello"; // ["Hello"]
convertArray < number > 1; // [1]

In this case, we specify that the return type is <T[]> meaning it is an array. So, if you pass a string <T[]> will become string[], if you pass number, <T[]> will be number[].

Alternatively, you can also write <T[]> as Array<T>

function convertArray<T>(value: T): Array<T> {
  return [value];
}

convertArray < string > "Hello"; // ["Hello"]
convertArray < number > 1; // [1]