DEV Community

Cover image for How to make Generic Functions that change return value type according to the parameter values type in TypeScript?
MELVIN GEORGE
MELVIN GEORGE

Posted on • Originally published at melvingeorge.me

How to make Generic Functions that change return value type according to the parameter values type in TypeScript?

Originally posted here!

To make a general function that changes the return value type according to the parameter type value, we can make use of a concept called Generic functions (aka General Functions) in TypeScript.

The basic idea of generic functions is to have a type safety by associating the type of the function parameters types (input) with the return value type (output) of the function.

In simple terms, Generic functions are those functions that set their type dynamically without us specifying it manually.

TL;DR

// Logic of the function is to get the first element from the array
// The Function is also a Generic Function
// and type safe where it can accept an array
// of any type and returns the same type
// of first element dynamically without
// we defining the type manually
function getFirstElement<GeneralType>(arr: GeneralType[]): GeneralType {
  return arr[0];
}

// call the function with array of numbers
const value1 = getFirstElement([1, 2, 3, 4]); // type => number 😍

// call the function with array of strings
const value2 = getFirstElement(["hello", "hai", "hey"]); // type => string 😍
Enter fullscreen mode Exit fullscreen mode

To understand it more clearly, let's consider a function that returns the first element from the array.

It may look like this,

// Logic of the function is to get the first element from the array
function getFirstElement(arr) {
  return arr[0];
}
Enter fullscreen mode Exit fullscreen mode

Now let's call the getFirstElement function and pass a array of numbers like this,

// Logic of the function is to get the first element from the array
function getFirstElement(arr) {
  return arr[0];
}

// call the function
const value1 = getFirstElement([1, 2, 3, 4]); // type => any 😕
Enter fullscreen mode Exit fullscreen mode

Now if we were to analyze the type of the value1 variable, we can see that the type is of any which is the expected behavior of the function since we haven't defined the parameter type as well as the return value type.

So to avoid this problem, let's define the parameter types and also types for the return value.

Let's make the parameter arr to hold an array of numbers and the return value of the function be a number.

It can be done like this,

// Logic of the function is to get the first element from the array
function getFirstElement(arr: number[]): number {
  return arr[0];
}

// call the function
const value1 = getFirstElement([1, 2, 3, 4]); // type => number 🙂
Enter fullscreen mode Exit fullscreen mode

Now if we look into the return type of the getFirstElement function call we can see that the type is of number which is good.

Now another problem arises where we also need to get the first value if the array contains elements that are of string type which we cannot do with our current function even though the mechanism or the logic we need is of the same.

In this case, we need to make the function into a general function where we need to associate the type of the parameter (input) with that of the return value type (output).

Let's make the getFirstElement function into a generic function by using a Type Parameter.

A Type parameter is declared by writing out the general type name inside an angled bracket symbol (<>) after the function name.

Let's name our Type parameter as GeneralType.

It can be done like this,

// Logic of the function is to get the first element from the array
function getFirstElement<GeneralType>(arr: number[]): number {
  return arr[0];
}

// call the function
const value1 = getFirstElement([1, 2, 3, 4]); // type => number 🙂
Enter fullscreen mode Exit fullscreen mode

Now that we have declared a general Type parameter.

We need to tell the function to accept an array of any value type and it should also return the correct type of the first element in the array.

So let's use the Type parameter called GeneralType wherever we have used the number type.

It can be done like this,

// Logic of the function is to get the first element from the array
// The Function is also a Generic Function
// and type safe where it can accept an array
// of any type and returns the same type
// of first element dynamically without
// we define the type manually
function getFirstElement<GeneralType>(arr: GeneralType[]): GeneralType {
  return arr[0];
}

// call the function
const value1 = getFirstElement([1, 2, 3, 4]); // type => number 🙂
Enter fullscreen mode Exit fullscreen mode

The getFirstElement function is now a Generic Function and can now accept an array of any value type and returns the same type of the first element from the array dynamically.

Now let's try to call the getFirstElement with both array of numbers and array of strings and analyze the return types.

It can be done like this,

// Logic of the function is to get the first element from the array
// The Function is also a Generic Function
// and type safe where it can accept an array
// of any type and returns the same type
// of first element dynamically without
// we define the type manually
function getFirstElement<GeneralType>(arr: GeneralType[]): GeneralType {
  return arr[0];
}

// call the function with array of numbers
const value1 = getFirstElement([1, 2, 3, 4]); // type => number 😍

// call the function with array of strings
const value2 = getFirstElement(["hello", "hai", "hey"]); // type => string 😍
Enter fullscreen mode Exit fullscreen mode

As you can see that the return type of the value1 and the value2 variables is according to the type of the array elements passed to the getFirstElement function.

Yay 🥳! We have made our function into a Generic Function.

See the above code live in codesandbox.

That's all 😃!

Feel free to share if you found this useful 😃.


Top comments (1)

Collapse
 
captainyossarian profile image
yossarian • Edited

Small improvement:

type Json = number | boolean | string | { [prop: string]: Json } | Array<Json>;

const getFirstElement = <Elem extends Json, Tuple extends Elem[]>(
  arr: [...Tuple]
): Tuple[0] => arr[0];

const value1 = getFirstElement([1, 2, 3, 4]); // 1

const value2 = getFirstElement(["hello", "hai", "hey"]); // "hello"
Enter fullscreen mode Exit fullscreen mode

As you might have noticed, TS is able to infer literal type with help of Variadic tuple types.
You can find more examples in my blog here and here