227
votes

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?

16

16 Answers

276
votes

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>;
}
72
votes

If you want to take a component class as a parameter (vs an instance), use React.ComponentClass:

function renderGreeting(Elem: React.ComponentClass<any>) {
    return <span>Hello, <Elem />!</span>;
}
40
votes

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")

23
votes

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
17
votes

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>;
}
15
votes

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!'
   }
});
10
votes

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.

7
votes

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>
5
votes

When declaring React Class component, use React.ComponentClass instead of React.Component then it will fix the ts error.

3
votes

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";
2
votes

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>
      }
    />
  )
}
2
votes

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} />
1
votes

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>;
}
0
votes

It is because of syntax

Here is an example to use TSX:

const SomeMadeComponent = () => (
    <div>
      <p>Some text</p>
    </div>
  );

And you use it like a normal component:

<SomeMadeComponent />

0
votes

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.

-1
votes

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>