Search⌘ K
AI Features

Creating generic functions

Explore how to create generic functions in TypeScript for React development. Learn to define type parameters, use generics in functions and arrow functions, and understand type inference to write reusable, strongly-typed functions that work with multiple data types.

A non-generic function

Below is a function that takes in an array and returns its first element. If the array is empty, null is returned.

function firstOrNull(array: string[]): string | null {
  return array.length === 0 ? null : array[0];
}

This function is strongly-typed, which is excellent; but what if we need to do the same thing for an array of numbers? We can’t use the above function because it is restricted for arrays of strings. Wouldn’t it be nice if we could pass the array item type into this function? Well, this is what generic functions allow us to.

Generic function syntax

We define the type parameters for a generic function in angle-brackets before the function’s parentheses:

function someFunc<T1, T2, ...>(...) {
  ...
}

The type parameters are placeholders for the real types. We can choose any names we want for these. These type parameter names can then be referenced within the function implementation.

Creating a generic function

Let’s turn our attention back to the function, firstOrNull, from the start of the lesson. We want to call this function and pass in the array item type as follows:

firstOrNull<string>(["Rod", "Jane", "Fred"]);
firstOrNull<number>([1, 2, 3]);

So, firstOrNull needs a single generic parameter. Let’s call this ItemType:

function firstOrNull<ItemType>( ... )

We can reference this for the array type that is passed into it:

function firstOrNull<ItemType>(array: ItemType[]) { ... }

We can also reference this in the function’s return type:

function firstOrNull<ItemType>(array: ItemType[]): ItemType | null { ... }

So, the final implementation of the generic function is below:

TypeScript 3.3.4
function firstOrNull<ItemType>(array: ItemType[]): ItemType | null {
return array.length === 0 ? null : array[0];
}
console.log(firstOrNull<string>(["Rod", "Jane", "Fred"]));

Below is another non-generic function:

TypeScript 3.3.4
function findFirst(array, match) {
return array.indexOf(match) === -1 ? null : array.filter(item => item === match)[0];
}
console.log(findFirst(["Rod", "Jane", "Fred"], "Jane"));

It checks to see if an item is in an array and returns it if so. If the item isn’t found, null is returned.

Modify this function and turn this into a strongly-typed function with the use of generics.

Generic arrow functions

Generic parameters can be passed into arrow functions as well:

const someVar = <T1, T2, ...>(...) => {
  ...
}

However, generic arrow functions are a little problematic.

Below is the firstOrNull function we worked on earlier in arrow function format:

const firstOrNull = <ItemType>(
  array: ItemType[]
): ItemType | null =>
  array.length === 0 ? null : array[0];

Copy and paste this into the TypeScript playground. Below is the link to go to the TypeScript playground: https://www.typescriptlang.org/play

Does the function compile okay?

A simple workaround for this problem is to put a comma after the generic parameter as follows:

const firstOrNull = <ItemType,>(
  array: ItemType[]
): ItemType | null =>
  array.length === 0 ? null : array[0];

This is the syntax for defining multiple parameters and doesn’t error, even though we haven’t defined two parameters. This is enough for the transpilation process to treat the angle brackets as generic parameters.

Type inference on generic functions

Let’s call our firstOrNull function without passing the array item types in the generic parameter:

TypeScript 3.3.4
function firstOrNull<ItemType>(array: ItemType[]): ItemType | null {
return array.length === 0 ? null : array[0];
}
const first = firstOrNull([1, 2, 3]);

What has TypeScript inferred the type of first to be?

So, TypeScript can cleverly infer the return type of a generic function if its parameters are based on the generic type.

Wrap up

Using generic types in a function allows them to be strongly-typed but still be used with different types. The generic types can be referenced within the function’s parameters, implementation, and the return type.

More information can be found on generic functions in the TypeScript handbook.

Next up, we’ll learn how we can use generics to create reusable interfaces.