27

Using Typescript, typing in Visual Studio, into a ".ts" file, consider the following declaration:

export const foo = <T>(myObject: T) => myObject.toString();

This works fine, type checking is fine, everything is great.

Now place that exact same code into a ".tsx" file that is being used for JSX and React.

Intellisense gets very upset and complains, because it is trying to make the <T> into a React JSX element. But my intention is to have the compiler treat it as a generic type designator.

The compiler complains with:

[gulp-typescript] 17008 JSX element 'T' has no corresponding closing tag.

I have tried numerous syntactical workarounds to try to get both the IDE and the compiler to let me escape the JSX and force the <T> to be understood by the compiler as a generic, as it would if JSX is not in use. But I can't find the magic sauce to do this.

Anyone smarter than I out there who can figure this out?

Stephan G
  • 3,289
  • 4
  • 30
  • 49
  • See https://stackoverflow.com/questions/32308370/what-is-the-syntax-for-typescript-arrow-functions-with-generics#comment99104831_45576880 – tokland Apr 04 '21 at 10:25

4 Answers4

29

When you have a single type parameter, TypeScript isn't sure whether it might be a JSX opening tag or not. It has to choose one, so it goes with JSX.

If you want a function with the exact same semantics, you can explicitly list the constraint of T:

const foo = <T extends {}>(myObject: T) => myObject.toString();

This breaks the ambiguity for TypeScript so that you can use a generic type parameter. It also has the same semantics because type parameters always have an implicit constraint of {}.

Daniel Rosenwasser
  • 21,855
  • 13
  • 48
  • 61
  • This is a very good solution. Thanks. NOTE: Resharper incorrectly flags this as an error. But it is not. – Stephan G Dec 13 '16 at 16:29
  • 6
    Don't use this. Nowadays (TS 3.5 and up) the implicit constraint is `unknown`: https://devblogs.microsoft.com/typescript/announcing-typescript-3-5/#generic-type-parameters-are-implicitly-constrained-to-unknown – ruohola Jan 25 '22 at 13:01
26

One workaround is to add a trailing comma:

export const foo = <T,>(myObject: T) => myObject.toString();
tokland
  • 66,169
  • 13
  • 144
  • 170
5

I personally use extends unknown. Sounds safest to me

const foo = <T extends unknown>(myObject: T) => myObject.toString();
Petur Subev
  • 19,983
  • 3
  • 52
  • 68
0

I can't think of a way around that, and will be glad as you to learn of one.
However, here's a different way to achieve the same:

export const foo = function<T>(myObject: T) { return myObject.toString(); }

The compiler won't complain about the generics.

Nitzan Tomer
  • 155,636
  • 47
  • 315
  • 299
  • 2
    It is true that this will work, but of course it won't behave in every way like an arrow function, and one would have to muck around with the proper binding of "this". But all that said, it may be the only solution. Unfortunately! – Stephan G Dec 13 '16 at 02:00
  • There's no `this` scope when declaring `export const` – Nitzan Tomer Dec 13 '16 at 08:42