Demystifying Generic Arrow Functions in TypeScript

In this guide, you'll learn how to create Typescript arrow functions generic

Posted August 8, 2024

Demystifying Generic Arrow Functions in TypeScript

Typescript provides you with type annotations to work with data types like numbers, strings, and booleans. This convection will definitely preserve your code’s type safety. A Number, for example, Typescript, gives you methods that exist on a number type. You will otherwise get a warning (between your input and returns) for using anything else not number related.

The challenge with this approach is, that the code template is not reusable to other types. If you want your code to accept strings, you’ll use number and string type annotations within your types. But this becomes really difficult to manage with excess type annotations. This is where you need generics.

Generics lets your code accept any type annotation while preserving Typescript type safety. Generics codify a relationship between the input and the return value. Whatever the input type is, that’s also going to be the return type.

A Typescript generics extends support to functions. When working with arrow functions, you’ll need a concise syntax to represent its anonymity. In this guide, you’ll learn how to create Typescript arrow functions generic.

Introduction to Typescript Generics

Generics lets you create type helpers that you can create other types out of. Before creating generic Typescript arrow functions, let’s check a few examples of how generics look like.

Take a look at this simple Typescript type:

const printArray = function (input: number): number[] {
  return [input];
};

printArray(2);

This example passes number types. You’ve just marked input as a number type. It’s properly returning an array of numbers, and there’s no error. This specific example is correct. However, if you want to call printArray with a string, you’ll get an error:

printArray accepts a number array and not a string array. If you want to accept an array of strings, the code will need string and number types array as follows:

const printArray = function (input: number | string): (number | string)[] {
 return [input];
};

printArray(2);
printArray('string example');

This route will prove problematic because you could have many different types to manage. Generics will take over from here. You’ll use a generic type parameter. The parameter will create a relationship between the input and the return value of the input. Whatever the type you have is what’s going to be the return type

Based on the printArray example, the function will now accept and return an array of any type, not just a number or string. All you need to do is add a type parameter with angled brackets in front of the list of parameters as follows:

const printArray = function <T>(input: T): T[] {
 return [input];
};

printArray(2);
printArray('string example');
printArray(true);

Here, T is the named type parameter, the generic. You can substitute it with any name you like. T, in this case, only gets specified when printArray is called:

Now that you understand generics, et’s replicate the same and use Typescript arrow functions.

Creating Generic Typescript Arrow Functions

The above examples use the traditional functions:

const testFctn = function <T>(input: T): T {
  return input;
};

Any basic syntax for a function generic will have the generic type within angle brackets, . To make the same example an arrow function, you’ll have the following example:

const testFctn = <T>(input: T): T => {
 return input;
}

The same applies to the arrow function syntax without a return value as such:

const testFctn = <T>(input: T): T => input

If let’s say, you want to add an extra argument, you’ll do the same and add an extra generic type:

const testFctn = <T, U>(input: T, value: U): [T, U] => [input, value];

Here:

  • T and U are the generic types.
  • The arguments input and value are of types T and U, respectively.
  • The arrow function will then return a tuple of T and U.

A Practical Use Case

So you can understand how generics work with arrow functions, let’s dive in and use a practical example. You’ll use Node.js Typescript based use case as follows:

  • Create a Node.js app using the npm init -y command.
  • Install the following dependencies to get Typescript ready within Node.js.
npm install typescript ts-node @types/node axios
  • Use the npx tsc --init command to initialize the Typescript tsconfig.json file.
  • Create an index.ts file within your working directory.

You need this example to be as simple as possible. The app will use Axios to process data from https://jsonplaceholder.typicode.com/users and display a list of users.

Go to your index.ts file and ensure your app takes the following shape:

import axios from "axios";

interface ApiResponse<T> {
  data: T;
  status: number;
}

const getUsers = async <T>(url: string): Promise<ApiResponse<T>> => {
  const response = await axios.get<T>(url);
  return {
    data: response.data,
    status: response.status,
  };
};

interface User {
  id: number;
  name: string;
  username: string;
  email: string;
}

getUsers<User[]>("https://jsonplaceholder.typicode.com/users")
  .then((response) => {
    response.data.forEach((user) => {
      console.log(user.name);
    });
  })
  .catch((error) => console.error(error));

You should now run the npx ts-node index.ts command within the index.ts path. Node.js should get the users by their names on the console:

Back to your code. On a closer look, you will note two generic use cases as follows:

  • A generic Interface:
interface ApiResponse<T> {
  data: T;
  status: number;
}

This example will use

  • An Interface ApiResponse. T will act as a generic type of any type.

  • The interface will then use the data property to hold any data type while referencing T.

  • You will status using a number type. You can do this while still using generic to create default types.

  • Using a generic arrow functions:

const getUsers = async <T>(url: string): Promise<ApiResponse<T>> => {
  const response = await axios.get<T>(url);
  return {
    data: response.data,
    status: response.status,
  };
};

This sample code will now use an arrow function with a generic typed function:

  • The getUsers function accepts a URL as a string and returns a Promise of the interface ApiResponse<T>.
  • <T> makes getUsers generic when getUsers is called.
  • xios.get<T>(url) runs a GET HTTP request using Axios. Note that the request will return data of generic type T.

If you are using to use the arrow function generics with a fronted, I have created a simple react themed app. Check the code examples with generics usage.

Why Use Typescript Generics with Arrow Functions

  • You get type safety and type-checked code at compile time.
  • You don’t need to use any. Generics are well defined and type safety tight.
  • Functions get reused by just changing the type argument.
  • You get to handle different data structures within the same code.
  • Typescript generics have IDE autocompletion features.
  • Your code uses consistent function signatures.

Conclusion

You have learned how to use generic with Typescript and more so with arrow functions. I hope the post was helpful and you gained the right insights.