Typescript: Generic usage reported as JSX Error

Created on 10 May 2017  路  3Comments  路  Source: microsoft/TypeScript



TypeScript Version: 2.2.2 & 2.3.2

Code

// Basic example
const paramArray = <T>(param1: T, param2: T) => [param1, param2];

// React specific example
type Component<T> = React.ComponentClass<T> | React.StatelessComponent<T>;
const decorator = <T>(Component: Component<T>) : Component<T> => (props) => <Component {...props} />

Expected behavior:

Both examples should work in a .tsx file in addition to the basic example working in a .ts file.

Actual behavior:

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.

Specifying an unused type variable seems to be a workaround to avoid the error:

const paramArray = <T1, T2>(param1: T1, param2: T1) => [param1, param2];
Design Limitation

Most helpful comment

In case this helps anyone arriving here, you can use a trailing comma after the type name to get around the ambiguity. For example: const f = <T,>(arg: T): T => {...}. Thanks to Thomas in this StackOverflow comment for the pointer.

All 3 comments

This is a limitation cased by the use of <T> for generic type parameter declarations. combined with JSX grammar, it makes such components ambiguous to parse.
The workaround is to use a function expression instead:

// Basic example
const paramArray = function <T>(param1: T, param2: T) { return [param1, param2] };

// React specific example
type Component<T> = React.ComponentClass<T> | React.StatelessComponent<T>;
const decorator = function<T>(Component: Component<T>) : Component<T> { return (props) => <Component {...props} /> };

@mhegazy I can't agree that this a design limitation or that it is ambiguous. It might be harder to parse, but it's definitely possible. Both Babylon and Flow parsers do this correctly. The reason is declaration of a JSX component like <T> must be followed by either a string literal or a token such as {, and end with the corresponding JSX closing tag </T>.

Babel's transpilation of these two cases:

const example1 = <T1>(param1: T1, param2: T1) => [param1, param2]
const example2 = <T1>(param1: T1, param2: T1) => [param1, param2]</T1>

=>

var example1 = function example1(param1, param2) {
  return [param1, param2];
};
var example2 = React.createElement(
  T1,
  null,
  "(param1: T1, param2: T1) => [param1, param2]"
);

Could you consider re-opening this as a bug?

In case this helps anyone arriving here, you can use a trailing comma after the type name to get around the ambiguity. For example: const f = <T,>(arg: T): T => {...}. Thanks to Thomas in this StackOverflow comment for the pointer.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

CyrusNajmabadi picture CyrusNajmabadi  路  3Comments

kyasbal-1994 picture kyasbal-1994  路  3Comments

uber5001 picture uber5001  路  3Comments

manekinekko picture manekinekko  路  3Comments

DanielRosenwasser picture DanielRosenwasser  路  3Comments