I wrote some code:
function renderGreeting(Elem: React.Component<any, any>) {
return <span>Hello, <Elem />!</span>;
}
I'm getting an error:
JSX element type
Elem
does not have any construct or call signatures
What does it mean?
This is a confusion between constructors and instances.
Remember that when you write a component in React:
class Greeter extends React.Component<any, any> {
render() {
return <div>Hello, {this.props.whoToGreet}</div>;
}
}
You use it this way:
return <Greeter whoToGreet='world' />;
You don't use it this way:
let Greet = new Greeter();
return <Greet whoToGreet='world' />;
In the first example, we're passing around Greeter
, the constructor function for our component. That's the correct usage. In the second example, we're passing around an instance of Greeter
. That's incorrect, and will fail at runtime with an error like "Object is not a function".
The problem with this code
function renderGreeting(Elem: React.Component<any, any>) {
return <span>Hello, <Elem />!</span>;
}
is that it's expecting an instance of React.Component
. What you want is a function that takes a constructor for React.Component
:
function renderGreeting(Elem: new() => React.Component<any, any>) {
return <span>Hello, <Elem />!</span>;
}
or similarly:
function renderGreeting(Elem: typeof React.Component) {
return <span>Hello, <Elem />!</span>;
}
When I'm converting from JSX to TSX and we keep some libraries as js/jsx and convert others to ts/tsx I almost always forget to change the js/jsx import statements in the TSX\TS files from
import * as ComponentName from "ComponentName";
to
import ComponentName from "ComponentName";
If calling an old JSX (React.createClass) style component from TSX, then use
var ComponentName = require("ComponentName")
The following worked for me: https://github.com/microsoft/TypeScript/issues/28631#issuecomment-472606019 I fix it by doing something like this:
const Component = (isFoo ? FooComponent : BarComponent) as React.ElementType
If you are passing functional component as props to another component use following:
import React from 'react';
type RenderGreetingProps = {
element: React.FunctionComponent<any>
};
function RenderGreeting(props: RenderGreetingProps) {
const {element: Element} = props;
return <span>Hello, <Element />!</span>;
}
If you really don't care about props then the widest possible type is React.ReactType
.
This would allow passing native dom elements as string. React.ReactType
covers all of these:
renderGreeting('button');
renderGreeting(() => 'Hello, World!');
renderGreeting(class Foo extends React.Component {
render() {
return 'Hello, World!'
}
});
If you are using material-ui, go to type definition of the component, which is being underlined by TypeScript. Most likely you will see something like this
export { default } from './ComponentName';
You have 2 options to resolve the error:
1.Add .default
when using the component in JSX:
import ComponentName from './ComponentName'
const Component = () => <ComponentName.default />
2.Rename the component, which is being exported as "default", when importing:
import { default as ComponentName } from './ComponentName'
const Component = () => <ComponentName />
This way you don't need to specify .default
every time you use the component.
As @Jthorpe alluded to, ComponentClass
only allows either Component
or PureComponent
but not a FunctionComponent
.
If you attempt to pass a FunctionComponent
, typescript will throw an error similar to...
Type '(props: myProps) => Element' provides no match for the signature 'new (props: myProps, context?: any): Component<myProps, any, any>'.
However, by using ComponentType
rather than ComponentClass
you allow for both cases. Per the react declaration file the type is defined as...
type ComponentType<P = {}> = ComponentClass<P, any> | FunctionComponent<P>
This is question the first result when I search for the error, so I would like to share the solution in my particular case:
The library I'm using look like this:
export { default as Arc } from './shapes/Arc';
I imported it incorrectly, which cause the error:
import Arc from "@visx/shape";
What it should be is
import { Arc } from "@visx/shape";
In my case, I was using React.ReactNode
as a type for a functional component instead of React.FC
type.
In this component to be exact:
export const PropertiesList: React.FC = (props: any) => {
const list:string[] = [
' Consequat Phasellus sollicitudin.',
' Consequat Phasellus sollicitudin.',
'...'
]
return (
<List
header={<ListHeader heading="Properties List" />}
dataSource={list}
renderItem={(listItem, index) =>
<List.Item key={index}> {listItem } </List.Item>
}
/>
)
}
In my case I was missing new
inside the type definition.
some-js-component.d.ts
file:
import * as React from "react";
export default class SomeJSXComponent extends React.Component<any, any> {
new (props: any, context?: any)
}
and inside the tsx
file where I was trying to import the untyped component:
import SomeJSXComponent from 'some-js-component'
const NewComp = ({ asdf }: NewProps) => <SomeJSXComponent withProps={asdf} />
You can use
function renderGreeting(props: {Elem: React.Component<any, any>}) {
return <span>Hello, {props.Elem}!</span>;
}
However, does the following work?
function renderGreeting(Elem: React.ComponentType) {
const propsToPass = {one: 1, two: 2};
return <span>Hello, <Elem {...propsToPass} />!</span>;
}
Looks like there's now a special new TypeScript type to address the need of this question: JSXElementConstructor
. If you are letting someone pass in the constructor to an unknown ReactElement rather than an instance of that ReactElement this is the correct type to pass.
const renderGreeting = (Elem: JSXElementConstructor<any, any>) => {
return <span>Hello, <Elem />!</span>;
}
This is equivalent to the above selected correct answer because using <Elem />
is equivalent to calling the constructor of a JSX Element with the new
keyword.
I solved it by making use of Type Assertions before exporting the component. TypeScript wasn't able to identify after composing it using redux 'compose' therefore I divided props types into IParentProps and IProps and use IParentProps while doing Type Assertions
import { compose } from 'react-redux'
import HOC1 from 'HOCs/HOC1'
import HOC2 from 'HOCs/HOC2'
type IParentProps = {}
type IProps = {}
const Component: React.FC<IProps & IParentProps> = React.memo((props) => {
return <SomeComponent {...props}/>
})
return compose(HOC1,HOC2)(Component) as React.FunctionComponent<IParentProps>