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?
Top comments (0)