Right, let’s talk about generic arrow functions in the wild west of TSX files. You’re going to write a lot of these, especially for React components and helper functions. And this is where TypeScript’s two syntaxes—one for the language and one for its type system—decide to have a bit of a scuffle. It’s not hard, but the first time your editor lights up with a red squiggly line, you’ll think you’ve broken the very fabric of reality. You haven’t. The designers just made a… questionable choice.

The core issue is that JSX, the syntax for HTML-like elements in React (<div>), uses angle brackets (<>). TypeScript’s generics also use angle brackets (<>). When you’re in a .tsx file, the parser has to figure out if you’re writing a JSX element or a generic type. It defaults to assuming JSX, because that’s the whole point of being in a TSX file. This is the root of all the confusion you’re about to experience.

The Syntax That Will Save Your Sanity

Let’s say you want a simple arrow function that takes an array of any type and returns the first element. Outside of a TSX file, you might try this:

// This will work in a .ts file, but FAIL in a .tsx file.
const first = <T>(arr: T[]): T | undefined => {
    return arr[0];
};

Paste that into a .tsx file and watch the fireworks. TypeScript will think <T> is a JSX tag named T, not a generic parameter. It’s absurd, but it’s the law of the land here. To resolve this ambiguity, you must add a trailing comma to the generic parameter, like so:

// This works everywhere: .ts and .tsx
const first = <T,>(arr: T[]): T | undefined => {
    return arr[0];
};

See that comma after <T,>? That’s the magic. It’s a signal to the TypeScript parser that says, “Hey, I know it looks like JSX, but this is actually a generic type parameter list. Keep reading.” It’s a hilariously low-tech solution, but it works flawlessly. You can also use the longer form with extends, which also makes it unambiguous: <T extends unknown>.

Explicitly Typing the Function

Sometimes, especially for more complex functions, you might want to type the entire function signature explicitly. This is often clearer and avoids the comma issue altogether because you’re declaring the generic before the arrow.

// Defining the generic type for the function itself
type FirstFunction = <T>(arr: T[]) => T | undefined;

// Then implementing it
const first: FirstFunction = (arr) => {
    return arr[0];
};

In this case, the generic parameter <T> is part of the type alias FirstFunction. The implementation (first) doesn’t need to redeclare it; it just knows that arr is of some type T[] and it must return a T | undefined. This is a rock-solid pattern for exported library functions where you want the type signature to be exceptionally clear.

A Real-World TSX Example: A Generic List Component

Let’s move beyond trivial examples. Here’s a practical React component that renders a list of any type of item, using a generic function to render each item.

import React from 'react';

// 1. The generic props. T is the type of our items.
interface GenericListProps<T> {
    items: T[];
    renderItem: (item: T) => React.ReactNode;
}

// 2. The component as a generic arrow function.
// Note the crucial comma: <T,>
const GenericList = <T,>({ items, renderItem }: GenericListProps<T>) => {
    return (
        <ul>
            {items.map((item, index) => (
                <li key={index}>{renderItem(item)}</li>
            ))}
        </ul>
    );
};

// 3. Usage with a specific type
interface User {
    id: number;
    name: string;
}

const UserList = () => {
    const users: User[] = [{ id: 1, name: 'Alice' }];

    return (
        <GenericList
            items={users}
            renderItem={(user) => <strong>{user.name}</strong>} // `user` is correctly inferred as type `User`
        />
    );
};

This is the power of generics in TSX. The GenericList component doesn’t care if you give it User[], string[], or number[]. It will happily accept them all, and, crucially, the renderItem callback will be perfectly type-safe. The user argument inside renderItem is automatically inferred as type User because we passed a User[] as items. You get autocompletion on user.name and protection from trying to access user.somethingThatDoesntExist.

Best Practices and Pitfalls

  1. Default to the Comma: Make it a habit to always write <T,> in your TSX arrow functions. It’s the most straightforward and most common solution. It’s a tiny, almost invisible piece of syntax that prevents a world of pain.

  2. Don’t Forget the Const: Always declare your functions with const. Using function statements avoids this issue (function first<T>(arr: T[]) {...}), but the React ecosystem is built on const + arrow functions for components. Stay consistent.

  3. When to Use the Type Alias Method: If your function’s generic signature is getting complex (e.g., multiple generics with constraints), defining it as a type alias first can dramatically improve readability. It separates the declaration of the contract from the implementation.

  4. The Pitfall: Inferred Generics in JSX: The main gotcha isn’t writing the function—it’s calling it. If TypeScript can’t infer the generic type from the props, you might need to specify it explicitly at the call site: <GenericList<User> items={users} renderItem={...} />. This doesn’t happen often, but it’s the first thing to check if you get a type error on usage.