676

The typescript handbook currently has nothing on arrow functions. Normal functions can be generically typed with this syntax: example:

function identity<T>(arg: T): T {
    return arg;
}

What is the syntax for arrow functions?

Rahul Tripathi
  • 168,305
  • 31
  • 280
  • 331
Andreas Frische
  • 8,551
  • 2
  • 19
  • 27

16 Answers16

963

Edit

Per @Thomas comment, in newer TS compilers, we can simply do:

const foo = <T,>(x: T) => x;

Original Answer

The full example explaining the syntax referenced by Robin... brought it home for me:

Generic functions

Something like the following works fine:

function foo<T>(x: T): T { return x; }

However using an arrow generic function will not:

const foo = <T>(x: T) => x; // ERROR : unclosed `T` tag

Workaround: Use extends on the generic parameter to hint the compiler that it's a generic, e.g.:

const foo = <T extends unknown>(x: T) => x;
jbmilgrom
  • 20,608
  • 5
  • 24
  • 22
  • 16
    would it be possible to declare some predefined generic type for `const foo`? i.e. `type GenericFun = (payload: A) => T;` then make `const foo: GenericFun` still generic without providing a `T` type? – ciekawy Feb 28 '18 at 01:04
  • @ciekawy, AFAIK, it's not, and I think that makes sense. Generics are about allowing higher order types in a sense. `GenericFun` defines a set that includes _other types_ `GenericFun` and `GenericFun`, among others. Allowing generic types to constitute a type annotation directly - e.g. const `const foo: GenericFun` - however, would tend to erode the static safety otherwise provided by requiring specificity e.g `const foo: GenericFun` or `const foo: GenericFun` – jbmilgrom Jun 03 '18 at 23:36
  • 40
    Your second example is only an error in a `.tsx` file (TypeScript + JSX). In a `.ts` file it works fine, as you can see on the [TypeScript playground](http://www.typescriptlang.org/play/#src=const%20foo%20%3D%20%3CT%3E(x%3A%20T)%20%3D%3E%20x%3B%0D%0A). – danvk Jan 17 '19 at 13:53
  • 244
    Newer typescript compilers also support trailing comma `const foo = (x: T) => x;` to sidestep the JSX ambiguity. – Thomas May 21 '19 at 16:54
  • 6
    @danvk Worth noting this only holds true for those who have forbidden JSX in TS files - if a project is configured to allow JSX in TS files you'll still need the "extends" or the trailing comma – Matt Nov 19 '19 at 21:24
  • 2
    So is this the takeaway: "use .ts instead of .tsx extensions"? – Isaac Pak Jun 26 '20 at 20:52
  • 2
    certainly don't use .tsx if you don't intend to use any JSX. For me it helps to distinguish files which have some components vs files with some sort of functionality... – Nathan Chappell Aug 25 '20 at 07:17
  • Dear TS team, Would be nice to do this without using hacks. Arrow functions are quite common these days. – Atrag Feb 03 '21 at 12:56
  • 4
    how to set default generic type? `const foo = (x: T) => x` not work, i always get `unknown` – Enivia Mar 10 '21 at 03:39
  • Worth mentioning if you have the Prettier setup and it removes the trailing comma, the trailing comma workaround won't work. – Farzad Yousefzadeh Sep 19 '21 at 05:44
  • To set the default generic type, I had to `const foo = (x: T) => x`. Still annoying in 2021 – joshkarges Oct 14 '21 at 20:53
  • 3
    Based on the [TypeScript documentation](https://www.typescriptlang.org/docs/handbook/2/generics.html#generic-types), you can create a generic arrow function like so: `const foo: (x: T) => T = (x) => x` – eyettea Jan 05 '22 at 13:56
  • @ciekawy I have trouble understanding the difference between `type GenericFun1 = () => T` and `type GenericFun2 = () => T` ... ts seem to handle them differently as doing `type GenericFun3 = GenericFun2` will raise an error. Any hints ? – sebpiq Dec 08 '22 at 10:28
322

If you're in a .tsx file you cannot just write <T>, but this works:

const foo = <T, >(x: T) => x;

As opposed to the extends {} hack, this hack at least preserves the intent.

mb21
  • 34,845
  • 8
  • 116
  • 142
109

I found the example above confusing. I am using React and JSX so I think it complicated the scenario.

I got clarification from TypeScript Deep Dive, which states for arrow generics:

Workaround: Use extends on the generic parameter to hint the compiler that it's a generic, this came from a simpler example that helped me.

    const identity = < T extends {} >(arg: T): T => { return arg; }
k0pernikus
  • 60,309
  • 67
  • 216
  • 347
Robin Luiten
  • 5,198
  • 3
  • 20
  • 24
59

This works for me

const Generic = <T> (value: T) => {
    return value;
} 
Harshit Singhai
  • 1,150
  • 11
  • 7
  • 23
    If In `.ts` file this works. Otherwise one has to extend. – HalfWebDev Aug 10 '19 at 09:27
  • 2
    Works perfectly fine: https://typescript-play.js.org/#code/DYUwLgBAzgFg9gVzIsAVAngBxBAvBAHlQD4AKANwENgEQAuCVASj2ImpACcxSADKmjjBYcASygQAJAG9h2OADMIA2gF9eTANwAobbBQoM2UgCJKlE0yA – Alireza Dec 16 '19 at 10:10
  • this is working fine for me in .ts and .tsx files in vscode – Shubhan May 15 '20 at 10:18
  • 1
    JSX element 'T' has no corresponding closing tag.ts(17008) Cannot find name 'T'.ts(2304) does NOT work in tsx files for me in vs code – JesseBoyd Aug 05 '22 at 20:23
50

The language specification says on p.64f

A construct of the form < T > ( ... ) => { ... } could be parsed as an arrow function expression with a type parameter or a type assertion applied to an arrow function with no type parameter. It is resolved as the former[..]

example:

// helper function needed because Backbone-couchdb's sync does not return a jqxhr
let fetched = <
           R extends Backbone.Collection<any> >(c:R) => {
               return new Promise(function (fulfill, reject) {
                   c.fetch({reset: true, success: fulfill, error: reject})
               });
           };
Andreas Frische
  • 8,551
  • 2
  • 19
  • 27
25

so late, but with ES6 no need extends it still work for me.... :)

let getArray = <T>(items: T[]): T[] => {
    return new Array<T>().concat(items)
}

let myNumArr = getArray<number>([100, 200, 300]);
let myStrArr = getArray<string>(["Hello", "World"]);
myNumArr.push(1)
console.log(myNumArr)
Bear
  • 297
  • 3
  • 2
  • 5
    This does not work for me, I have to add a comma like so: ``. as described in @Thomas comment under @jbmilgrom' answer – apollo Jun 07 '20 at 21:40
  • 3
    You should read the other solutions before posting one. Your solution has already been posted with explanation. It works only inside of a .ts file, not a .tsx file. – Isaac Pak Jun 26 '20 at 20:55
  • doesn work, as doesn't a comma. Syntax error caught by VSCode – trainoasis Jan 14 '22 at 16:17
22

This works for me

 const logSomething = <T>(something:T): T => {
       return something;
    }
const.takeo
  • 269
  • 2
  • 3
12

while the popular answer with extends {} works and is better than extends any, it forces the T to be an object

const foo = <T extends {}>(x: T) => x;

to avoid this and preserve the type-safety, you can use extends unknown instead

const foo = <T extends unknown>(x: T) => x;
Michal Filip
  • 870
  • 8
  • 9
8

I to use this type of declaration:

const identity: { <T>(arg: T): T } = (arg) => arg;

It allows defining additional props to your function if you ever need to and in some cases, it helps keeping the function body cleaner from the generic definition.

If you don't need the additional props (namespace sort of thing), it can be simplified to:

const identity: <T>(arg: T) => T = (arg) => arg;
Roy Art
  • 578
  • 5
  • 10
  • When using `` inside the function body, this did not work for me. Typescript tells me `` isn't used for the `` at the function definition location and tells me that it cannot find `` at the position where I refer to it in the function body. With the `` 'hack' I do not have this issue. – Martijn Jan 19 '22 at 15:14
4

I know I am late to this answer. But thought of answering this in case anyone else finds it helpful. None of the answers mention how to use generics with an async arrow function.

Here it goes :

const example = async <T> (value: T) => {
    //awaiting for some Promise to resolve or reject;
     const result = await randomApi.getData(value);

} 
LearnToLive
  • 462
  • 9
  • 25
3

The non-arrow function way. Expanding on the example from the OP.

function foo<T>(abc: T): T {
    console.log(abc);
    return abc;
}

const x = { abc: 123 };
foo(x);

const y = 123;
foo<number>(y);

Aside from the answer of embedding the whole thing into one statement:

const yar = <T,>(abc: T) => {
    console.log(abc);
    return abc;
}

Another approach is to have an intermediate type:

type XX = <T>(abc: T) => T;

const bar: XX = (abc) => {
    console.log(abc);
    return abc;
}

Playground

Archimedes Trajano
  • 35,625
  • 19
  • 175
  • 265
2

In 2021, Ts 4.3.3

const useRequest = <DataType, ErrorType>(url: string): Response<DataType, ErrorType> 
   => {
      ...
   }
CodeFarmer
  • 2,644
  • 1
  • 23
  • 32
2

Adding an example for multiple depended generic types:

This function, was converted to arrow function as the following:

http.get = function <T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R> {
            config.withCredentials = true;
            ....
          };

Notice the extends instead of the equal sign:

http.get = async <T extends any, R extends unknown = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R> => {
            config.withCredentials = true;
            ...
          };
Ester Kaufman
  • 708
  • 10
  • 20
-1

enter image description here

Using <T, extends {}> throws an error when you try to pass null as parameter. I will prefer using <T,> because it clears the issue. I am yet to get the reason why. But this worked for me.

enter image description here

-3

Here I got 2 cases of arrow function with generics:

  • To call directly:
const foo = <T>(value: T): void => {
    console.log(value);
foo('hello') // hello
}
  • To create a type to use later:
type TFoo<S> = (value: S) => boolean;
const foo: TFoo<number> = (value) => value>0;
console.log(foo(1)) // true
console.log(foo(-1)) // false

Hopefully this helps somewhere!

Yuh Lee
  • 343
  • 1
  • 4
-3

If need make return method generic, it didn't show errors with extra data ex:

export type DataGenerator<T> = (props: {name: string}) => T;

const dataGenerator: DataGenerator<{city?: string}> = ({  name }) => {
    return {
        city: `${name}`,
        name: "aaa",
    }
}

dataGenerator({name: "xxx"})

I need to show error on name

Suhaib
  • 27
  • 5