Question

TypeScript Arrow Functions with Generics: Syntax and Examples

Question

In TypeScript, regular functions can use generics like this:

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

What is the correct syntax for writing an arrow function with generics that behaves the same way?

Short Answer

By the end of this page, you will understand how to write generic arrow functions in TypeScript, how their syntax compares to regular generic functions, where they are commonly used, and which mistakes beginners often make when combining generics, arrow functions, and type annotations.

Concept

Generics in TypeScript let you write functions that work with many types while still preserving type safety.

For example, this regular function:

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

says:

  • T is a type placeholder
  • whatever type comes in as arg
  • the same type comes back out

This is useful because the function is reusable without losing information about types.

Arrow functions support generics too. The generic type parameter is written before the parameter list:

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

Here is the structure:

const functionName = <T>(param: T): T => {
  // logic
};

This matters in real programming because arrow functions are used everywhere in modern TypeScript code:

  • utility functions
  • callbacks
  • React code
  • functional patterns like map, filter, and reduce
  • small reusable helpers

So if you know generics but only for function declarations, learning the arrow version helps you write the same type-safe logic in the style most codebases use today.

Mental Model

Think of a generic as a label slot on a machine.

  • A normal function machine can have a slot labeled T
  • An arrow function machine can have the same slot
  • You plug in a type when you use it, and the machine keeps that type consistent

So this:

function identity<T>(arg: T): T

and this:

const identity = <T>(arg: T): T =>

are just two differently shaped machines with the same type slot.

The important idea is: the generic belongs to the function itself, not to the variable name.

Take Quiz

Syntax and Examples

Basic syntax

A generic arrow function in TypeScript looks like this:

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

This is the arrow-function version of:

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

Example: identity function

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

const a = identity<string>("hello");
const b = identity<number>(42);
const c = identity(true);

What happens here?

  • identity<string>("hello") returns a string
  • identity<number>(42) returns a number
  • identity(true) lets TypeScript infer T as boolean

Example: generic arrow function with arrays

const firstItem = <T>(items: T[]): T | undefined => {
  return items[0];
};

const firstName = firstItem(["Ava", "Mia", "Leo"]);
const firstNumber = firstItem([10, 20, 30]);

This function works for any array type:

  • string[]
  • number[]
  • boolean[]
  • custom object arrays

Example: multiple generic type parameters

const pair = <T, U>(first: T, second: U): [T, U] => {
  return [first, second];
};

const result = pair("age", 25);

Here:

  • T becomes string
  • U becomes number
  • return type becomes [string, number]

Example: adding a type constraint

const getLength = <T extends { length: number }>(value: T): number => {
  return value.length;
};

getLength("hello");
getLength([1, 2, 3]);

This works only for values that have a length property.

Step by Step Execution

Consider this example:

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

const value = identity("TypeScript");

Step by step:

  1. identity is assigned an arrow function.
  2. <T> declares a generic type parameter named T.
  3. (arg: T) means the parameter arg must be of type T.
  4. : T after the parameter list means the function returns the same type T.
  5. => arg returns the argument unchanged.
  6. When calling identity("TypeScript"), TypeScript infers that T is string.
  7. So the function becomes effectively:
const value: string = identity("TypeScript");

Another example with explicit type arguments:

const wrapInArray = <T>(value: T): T[] => [value];

const numbers = wrapInArray<number>(5);

Trace:

  1. <T> defines a generic type slot.
  2. value: T means value must match that type.
  3. Return type T[] means an array of that same type.
  4. wrapInArray<number>(5) sets T to number.
  5. The return type becomes number[].
  6. Result: [5].

Real World Use Cases

Generic arrow functions appear often in practical TypeScript code.

1. Reusable utility helpers

const toArray = <T>(value: T): T[] => [value];

Useful for data transformation utilities.

2. Safe data selection

const getFirst = <T>(items: T[]): T | undefined => items[0];

Common in list processing, API data handling, and UI state logic.

3. Wrapping values in objects

const createResponse = <T>(data: T) => ({ data, success: true });

Useful in APIs, services, and helper layers.

4. Generic event or callback helpers

const callTwice = <T>(value: T, fn: (item: T) => void): void => {
  fn(value);
  fn(value);
};

Used in callback-heavy code and utility libraries.

5. React and frontend utilities

Arrow functions are very common in React projects, so generic arrow functions are often used in:

  • hooks
  • state helpers
  • prop transformers
  • reusable list logic

Example:

const useValue = <T>(initial: T) => {
  let current = initial;
  return {
    get: () => current,
    set: (value: T) => {
      current = value;
    }
  };
};
Take Quiz

Real Codebase Usage

In real codebases, generic arrow functions are usually used for small, composable helpers rather than large standalone functions.

Common patterns

Guard-style transformation helpers

const ensureArray = <T>(value: T | T[]): T[] => {
  return Array.isArray(value) ? value : [value];
};

This pattern normalizes input into a predictable shape.

Data mapping helpers

const pluck = <T, K extends keyof T>(obj: T, key: K): T[K] => obj[key];

This is common when working with objects and typed configuration.

Validation wrappers

const withDefault = <T>(value: T | undefined, fallback: T): T => {
  return value === undefined ? fallback : value;
};

Useful in configuration loading and API parsing.

Generic callbacks stored in constants

const identity = <T>(x: T): T => x;

This is one reason arrow syntax matters: many teams prefer assigning utilities to const variables.

Why teams use arrow functions here

  • consistent style with other callbacks and helpers
  • easier to pass around as values
  • works naturally with closures
  • common in modern frontend and backend TypeScript projects

If a function is small and reusable, a generic arrow function is often a clean choice.

Common Mistakes

1. Putting the generic in the wrong place

Incorrect:

const identity = (arg: T): T => arg;

Problem:

  • T is used, but never declared

Correct:

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

2. Confusing variable type annotation with function generics

You may also see this style:

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

This is valid, but it means:

  • the variable identity is given a generic function type
  • the implementation is assigned to that variable

Beginners often mix this up with the direct syntax.

Direct syntax is usually easier to read:

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

3. Forgetting JSX parsing issues in .tsx files

In .tsx files, this can be ambiguous:

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

The parser may think <T> is JSX.

A common fix is to add a trailing comma:

const identity = <T,>(arg: T): T => arg;

Another option is to use a regular function declaration.

4. Overusing explicit type arguments

This is valid:

const result = identity<string>("hello");

But often unnecessary:

const result = identity("hello");

TypeScript can infer the type automatically in many cases.

5. Returning a different type than promised

Broken example:

const badIdentity = <T>(arg: T): T => {
  return "wrong";
};

Problem:

  • the function promises to return T
  • but always returns a string

The implementation must actually match the generic contract.

Comparisons

Generic regular function vs generic arrow function

StyleSyntaxBest for
Regular functionfunction identity<T>(arg: T): T { return arg; }Named reusable functions, hoisting, clearer declarations
Arrow functionconst identity = <T>(arg: T): T => arg;Utilities, callbacks, modern const-based code style

Direct generic arrow syntax vs typed variable syntax

ApproachExampleNotes
Generic on the arrow functionconst fn = <T>(arg: T): T => arg;Usually easiest to read
Generic on the variable typeconst fn: <T>(arg: T) => T = (arg) => arg;Useful when enforcing a function type separately

Explicit type argument vs type inference

StyleExampleWhen to use
Explicitidentity<number>(5)When inference is unclear or you want to be specific
Inferredidentity(5)Most common and usually simpler

Arrow functions vs regular functions in TypeScript

FeatureRegular functionArrow function
GenericsYesYes
this bindingDynamicLexically bound
HoistedYesNo, if assigned to const
Common in callbacksSometimesVery common
Take Quiz

Cheat Sheet

Generic arrow function syntax

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

Multiple type parameters

const fn = <T, U>(a: T, b: U): [T, U] => [a, b];

Constraint syntax

const fn = <T extends { length: number }>(value: T): number => value.length;

Generic function type on a variable

const fn: <T>(arg: T) => T = (arg) => arg;

In .tsx files

Prefer:

const fn = <T,>(arg: T): T => arg;

because <T> may be parsed as JSX.

Quick rules

  • Put the generic parameter list before the function parameters
  • Use T only after declaring it with <T>
  • Let TypeScript infer types when possible
  • Use constraints like extends when the function needs specific properties
  • In React .tsx files, use <T,> if needed to avoid JSX ambiguity

FAQ

What is the syntax for a generic arrow function in TypeScript?

Use this pattern:

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

Can TypeScript infer the generic type in an arrow function?

Yes. In many cases you do not need to write the type argument explicitly.

const result = identity(123);

Here, T is inferred as number.

Why does <T> sometimes fail in a .tsx file?

Because the parser may interpret <T> as JSX instead of a generic type parameter. A common fix is:

const identity = <T,>(arg: T): T => arg;

Is a generic arrow function different from a generic regular function?

Not in capability. Both can use generics. The main difference is syntax and how they behave with things like this and hoisting.

Can arrow functions have more than one generic type?

Yes.

const pair = <T, U>(a: T, b: U): [T, U] => [a, b];

Should I use explicit type arguments every time?

No. Type inference is often enough. Use explicit type arguments only when inference is unclear or when you want to force a specific type.

Take Quiz

Mini Project

Description

Build a small set of generic arrow-function utilities in TypeScript. This project demonstrates how generic arrow functions can safely work with different types while preserving accurate return types. It reflects the kind of small helper functions developers often write in real projects.

Goal

Create and use several generic arrow functions that work with strings, numbers, arrays, and objects without losing type safety.

Requirements

  • Create a generic arrow function that returns the input unchanged.
  • Create a generic arrow function that wraps a value in an array.
  • Create a generic arrow function that returns the first item of an array.
  • Create a generic arrow function that reads a value from an object using a typed key.
  • Call each function with real examples and log the results.
Take Quiz

Keep learning

Related questions

View more