TypeScript Generic Function Reported As JSX Error
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?