Using generics in Arrow functions in TypeScript

avatar
Borislav Hadzhiev

Last updated: Feb 29, 2024
3 min

banner

# Table of Contents

  1. Using generics in Arrow functions in TypeScript
  2. Applying constraints around generics in arrow functions
  3. Using generics in arrow functions in classes
  4. When working in .tsx files, use a trailing comma

# Using generics in Arrow functions in TypeScript

You can use a generic in an arrow function by setting it right before the function's parameters.

The generic can be passed when the function is invoked.

index.ts
const returnInArray = <T,>(value: T): T[] => { return [value]; }; const strArray = returnInArray<string>('bobbyhadz.com'); const numArray = returnInArray<number>(100);

using generics in arrow functions

The code for this article is available on GitHub

Generics allow us to pass types as a variable to functions and classes.

The generics are specified right before the function's parameters using arrow brackets.

The syntax for calling a function that has a generic is the same - it's passed right before the function's arguments.

Note that we didn't have to explicitly provide the generic when calling the function.

In this situation, TypeScript would be able to infer the type of the generic based on the type of the passed-in argument.

index.ts
const returnInArray = <T,>(value: T): T[] => { return [value]; }; // ๐Ÿ‘‡๏ธ const strArray: string[] const strArray = returnInArray('bobbyhadz.com'); // ๐Ÿ‘‡๏ธ const numArray: number[] const numArray = returnInArray(100);

# Applying constraints around generics in arrow functions

We can apply constraints around generics to only allow certain types to be passed.

index.ts
type CanRun = { run(): void; }; // ๐Ÿ‘‡๏ธ Can only be called with an object that has run() method const callRun = <T extends CanRun>(obj: T) => { obj.run(); }; // ๐Ÿ‘‡๏ธ the animal runs callRun({ run: () => console.log('the animal runs') }); class Dog { run() { console.log('the dog runs'); } } callRun(new Dog()); // ๐Ÿ‘‰๏ธ the dog runs

applying constraints around generics in arrow functions

The code for this article is available on GitHub

The callRun() arrow function can only be called with an object that has a property run that is a function with a return type of void.

If we try to call the function with a value that doesn't satisfy the CanRun type, we would get an error.

index.ts
type CanRun = { run(): void; }; const callRun = <T extends CanRun>(obj: T) => { obj.run(); }; // โ›”๏ธ Argument of type 'number' is not assignable // to parameter of type 'CanRun'.ts(2345) callRun(100);

This pattern is very commonly used when you need a guarantee that a function can only be called with an object that contains certain properties.

The object could be of multiple types, but as long as it contains the specified properties, it can be passed as an argument to the function.

index.ts
class Shark { swim() { console.log('The shark swims'); } } class Dolphin { swim() { console.log('The dolphin swims'); } } interface CanSwim { swim(): void; } const callSwim = <T extends CanSwim>(obj: T): void => { obj.swim(); }; callSwim<Dolphin>(new Dolphin()); callSwim<Shark>(new Shark());

The callSwim function takes an object parameter and calls the swim method on it.

The function uses a constraint to make sure that it only gets passed objects that contain a swim property of type function.

# Using generics in arrow functions in classes

You can also use generics in arrow functions in classes.

index.ts
class GenericArray { public arr: (string | number)[] = []; insert = <T extends string | number>(el: T): void => { this.arr.push(el); }; print = (): void => { console.log(this.arr); }; } const ga1 = new GenericArray(); ga1.insert<string>('a'); ga1.insert<number>(1); ga1.print(); // ๐Ÿ‘‰๏ธ ['a', 1] // โ›”๏ธ Argument of type '{ hello: string; }' is not assignable // to parameter of type 'string | number'.ts(2345) ga1.insert({ hello: 'world' });
The code for this article is available on GitHub

The insert class method can either be passed a string or a number.

# When working in .tsx files, use a trailing comma

Notice that we used a trailing comma after the T type in the generic.

index.tsx
const returnInArray = <T,>(value: T): T[] => { return [value]; }; const strArray = returnInArray<string>('bobbyhadz.com'); const numArray = returnInArray<number>(100);

Newer TypeScript compilers support a trailing comma after the last type in the generic.

This is necessary for .tsx files, otherwise, you'd get syntactical errors.

If you have multiple types, you don't have to use a trailing comma in .tsx files, e.g. <T,Y> works.

Alternatively, you can extend an empty object if you still get syntactical errors in .tsx files.`

index.tsx
const returnInArray = <T extends unknown>(value: T): T[] => { return [value]; }; const strArray = returnInArray<string>('bobbyhadz.com'); const numArray = returnInArray<number>(100);

The T extends unknown constraint does nothing and is only necessary if you get syntactical errors when working in .tsx files.

I've also written a detailed article on how to set the return type of an arrow function.

If you need to pass a function as a parameter, check out the following article.

# Additional Resources

You can learn more about the related topics by checking out the following tutorials:

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.

Copyright ยฉ 2024 Borislav Hadzhiev