Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic usage reported as JSX Error #15713

Closed
jrwebdev opened this issue May 10, 2017 · 7 comments · May be fixed by #59485
Closed

Generic usage reported as JSX Error #15713

jrwebdev opened this issue May 10, 2017 · 7 comments · May be fixed by #59485
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@jrwebdev
Copy link

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];
@mhegazy
Copy link
Contributor

mhegazy commented May 10, 2017

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 mhegazy added the Design Limitation Constraints of the existing architecture prevent this from being fixed label May 10, 2017
@mhegazy mhegazy closed this as completed May 17, 2017
@niieani
Copy link

niieani commented Dec 22, 2017

@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?

@reverie
Copy link

reverie commented Jun 6, 2019

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.

@danielo515
Copy link

danielo515 commented Jan 15, 2022

So this is going to be a permanent issue for the foreseeable future?

@spencergray96
Copy link

spencergray96 commented Oct 7, 2022

This works if you need to make a generic component that is also connected to Redux, only "caveat" is having to define the component multiple times, once for each type you are going to use:

interface Props<T> {
	test: T;
}

interface StateProps {
	fullToken: Token;
}

function CoolComponent<T>(props: CoolComponentProps<T>): ReactElement {
	return (
		<div>
			{props.test}
		</div>
	);
}

function connector<T>() {
	return connect((store: IStore, props: Props<T>): StateProps & Props<T> => {
		return {
			fullToken: store.metaStore.fullToken,
			...props,
		}
	});
}

type CoolComponentProps<T> = ConnectedProps<ReturnType<typeof connector>>;

export const CoolComponentString = connector<string>()(CoolComponent);
export const CoolComponentNumber = connector<number>()(CoolComponent);

/* Implementation */

function Implementation(): ReactElement {
	return (
		<React.Fragment>
			<CoolComponentString test="hello"/>
			
			<CoolComponentNumber test={123}/>
		</React.Fragment>
	)
}

@bingDBdu
Copy link

bingDBdu commented Apr 18, 2024

So there is a bug. we found it. We avoided it but we won't intend to fix it.
Everybody meets it and tries to find the solution here. Holy TS

@shiv-raj45
Copy link

extending empty object with generic worked for me
const AppTable = <T extends {}>({ columns, data }: IProps<T>) => { return <DataTable columns={columns} data={data} />; };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants