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:
Tis 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, andreduce - 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.
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 astringidentity<number>(42)returns anumberidentity(true)lets TypeScript inferTasboolean
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:
TbecomesstringUbecomesnumber- 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:
identityis assigned an arrow function.<T>declares a generic type parameter namedT.(arg: T)means the parameterargmust be of typeT.: Tafter the parameter list means the function returns the same typeT.=> argreturns the argument unchanged.- When calling
identity("TypeScript"), TypeScript infers thatTisstring. - 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:
<T>defines a generic type slot.value: Tmeansvaluemust match that type.- Return type
T[]means an array of that same type. wrapInArray<number>(5)setsTtonumber.- The return type becomes
number[]. - 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;
}
};
};
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:
Tis 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
identityis 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
| Style | Syntax | Best for |
|---|---|---|
| Regular function | function identity<T>(arg: T): T { return arg; } | Named reusable functions, hoisting, clearer declarations |
| Arrow function | const identity = <T>(arg: T): T => arg; | Utilities, callbacks, modern const-based code style |
Direct generic arrow syntax vs typed variable syntax
| Approach | Example | Notes |
|---|---|---|
| Generic on the arrow function | const fn = <T>(arg: T): T => arg; | Usually easiest to read |
| Generic on the variable type | const fn: <T>(arg: T) => T = (arg) => arg; | Useful when enforcing a function type separately |
Explicit type argument vs type inference
| Style | Example | When to use |
|---|---|---|
| Explicit | identity<number>(5) | When inference is unclear or you want to be specific |
| Inferred | identity(5) | Most common and usually simpler |
Arrow functions vs regular functions in TypeScript
| Feature | Regular function | Arrow function |
|---|---|---|
| Generics | Yes | Yes |
this binding | Dynamic | Lexically bound |
| Hoisted | Yes | No, if assigned to const |
| Common in callbacks | Sometimes | Very common |
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
Tonly after declaring it with<T> - Let TypeScript infer types when possible
- Use constraints like
extendswhen the function needs specific properties - In React
.tsxfiles, 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.
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.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.