TypeScript Generic Function Reported As JSX Error

June 01, 2021

Motivation

const genericFn = <T>() => { return "This is a poorly written example generic function" }

Above is an example of a function with a generic parameter T that could potentially be used within the function body. However, if the above code is saved in a .tsx file (In my context, within a React application, while trying to create a generic custom hook), you will receive with the following error when hover over <T>:

JSX element 'T' has no corresponding closing tag.ts(17008) Cannot find name 'T'.ts(2304)

Exploration

Defining generic function in TypeScript

To resolve the issue, I started with researching on how to properly define a generic function in TypeScript. Perhaps I made a mistake somewhere in the above syntax. I landed on two articles here and here. Both of them talked about how to create a custom React hook that uses generics. However, the syntax used in the articles were similar to the above example but no errors were discussed.

While I did not find an answer after heading over to TypeScript-CheatSheets, I thought the note on avoiding type inference when declaring custom hook was an interesting point that I did not know about.

If you are returning an array in your Custom Hook, you will want to avoid type inference as TypeScript will infer a union type (when you actually want different types in each position of the array). Instead, use TS 3.4 const assertions:

export function useLoading() { const [isLoading, setState] = React.useState(false); const load = (aPromise: Promise<any>) => { setState(true); return aPromise.finally(() => setState(false)); }; return [isLoading, load] as const; // infers [boolean, typeof load] instead of (boolean | typeof load)[] }

This way, when you destructure you actually get the right types based on destructure position.

Credit: TypeScript-CheatSheets


Google the error

Moving on with the second strategy: “Google & Stack overflow”. Searching the above error landed me on the following issue in the Microsoft TypeScript repository. There were a few more interesting links here and here.

So according to the reported issue:

The usage of <T> prior to the function braces causes a JSX error within .tsx files: “JSX element has no corresponding closing tag.”. Basic example works as expected in a .ts file.

The issue was claimed to be a limitation and there were a few workarounds mentioned (in the thread and also in the related stack overflow post):

  • change from .tsx to .ts
  • add a comma: const f = <T,>(arg: T): T => {...}
  • extend this way: const foo = <T extends unknown>(x: T) => x;
  • or extend this way: const foo = <T extends {}>(x: T): T => x;

Thoughts

Funny how issues like this one will continue to bite us even way into the future… simply because no one is going to do anything about it?



Profile picture

Written by Liu Yongliang who lives in Singapore. Also on Dev.to, LinkedIn, GitHub

© Copyright 2022 Liu Yongliang