头图

The text starts here~

Overview

"JSX element type does not have any construct or call signatures" error is generated when we try to pass an element or react component as an attribute to another component, but the type of the attribute is declared wrong. To resolve this error, the type React.ElementType can be used.

jsx-element-does-not-have-any-construct-or-call-signatures.png

Here is an example to show how the error occurs.

 // App.tsx
import React from 'react';

interface Props {
  comp: JSX.Element;
}

const Wrapper: React.FunctionComponent<Props> = props => {
  const {comp: Comp} = props;
  // ⛔️ JSX element type 'Comp' does not have any construct or call signatures.ts(2604)
  return (
    <div>
      <Comp name="James" />
    </div>
  );
};

const App: React.FunctionComponent = () => {
  const heading = ({name}: {name: string}) => <h2>Hello {name}</h2>;

  return (
    <div>
      <Wrapper comp={heading} />
    </div>
  );
};

export default App;

We tried to pass a React component as a property to the Wrapper component, but we declared the type of that React component as JSX.Element .

React.ElementType

To resolve the error, declare the property's type as React.ElementType .

 // App.tsx
import React from 'react';

interface Props {
  comp: React.ElementType; // 👈️ type it as React.ElementType
}

const Wrapper: React.FunctionComponent<Props> = props => {
  // 👇️ component names must start with capital letter
  const {comp: Comp} = props;
  return (
    <div>
      <Comp name="James" />
    </div>
  );
};

const App: React.FunctionComponent = () => {
  // 👇️ takes a name prop
  const heading = ({name}: {name: string}) => <h2>Hello {name}</h2>;

  return (
    <div>
      <Wrapper comp={heading} />
    </div>
  );
};

export default App;
Note that React.ElementType can pass a generic for the type of attribute expected by the element.

In this example, we have to pass it an object with a name property of type String, because that's the heading adf37a630bc83a33cb7d32b9f386e40d--- property that the component receives.

 // App.tsx
import React from 'react';

interface Props {
  // ✅ explicitly type props comp takes
  comp: React.ElementType<{name: string}>;
}

const Wrapper: React.FunctionComponent<Props> = props => {
  // 👇️ component names must start with capital letter
  const {comp: Comp} = props;
  return (
    <div>
      <Comp name="James" />
    </div>
  );
};

const App: React.FunctionComponent = () => {
  const heading = ({name}: {name: string}) => <h2>Hello {name}</h2>;

  return (
    <div>
      <Wrapper comp={heading} />
    </div>
  );
};

export default App;

Now we explicitly declare the type of attribute that the element accepts when it is used comp . This helps us take advantage of the IDE's autocomplete feature when passing properties to the component.

We could also use React.ComponentType , but then we need to declare the type on the property.

 // App.tsx
import React from 'react';

interface Props {
  // 👇️ now using React.ComponentType 👇️
  comp: React.ComponentType<{name: string}>;
}

const Wrapper: React.FunctionComponent<Props> = props => {
  // 👇️ component names must start with capital letter
  const {comp: Comp} = props;
  return (
    <div>
      <Comp name="James" />
    </div>
  );
};

const App: React.FunctionComponent = () => {
  const heading = ({name}: {name: string}) => <h2>Hello {name}</h2>;

  return (
    <div>
      <Wrapper comp={heading} />
    </div>
  );
};

export default App;

Generic types in React.ComponentType 803dc3394bbce2db39ba2297272632f7--- cannot default to any type, so we need to explicitly declare the type of the property.

Passing JSX elements

If you need to pass a JSX element as an attribute to a component, and it's not really a component, then using the JSX.Element type is correct.

 // App.tsx
import React from 'react';

interface Props {
  // 👇️ using JSX.Element type
  comp: JSX.Element;
}

const Wrapper: React.FunctionComponent<Props> = props => {
  const {comp: Comp} = props;

  // 👇️ use as {Comp}
  return <div>{Comp}</div>;
};

const App: React.FunctionComponent = () => {
  const Heading = ({name}: {name: string}) => <h2>Hello {name}</h2>;

  // 👇️ we are passing an actual JSX element
  // because we didn't pass it as comp={Heading}
  return (
    <div>
      <Wrapper comp={<Heading name="James" />} />
    </div>
  );
};

export default App;

We declared the type of the comp property as JSX.Element because we passed a real JSX element (not a component) to the Wrapper component.

We're passing a JSX element because we're passing comp={<Heading />} as an attribute, not comp={(props) => <h2>Hello world</h2>} .

Note that in the first case we are passing a JSX element attribute. And in the second case, we're passing a function that returns a JSX element (a functional component).

In Wrapper components, we should not try to use JSX elements as components. For example, instead of writing <Comp /> , write {Comp} .

We're not passing a real component as a property, we're passing a JSX element, so it shouldn't be used as a component.

update type package

If none of the previous suggestions helped, try updating your React type version by running the following command.

 # 👇️ with NPM
npm install react@latest react-dom@latest

npm install --save-dev @types/react@latest @types/react-dom@latest

# ----------------------------------------------

# 👇️ with YARN
yarn add react@latest react-dom@latest

yarn add @types/react@latest @types/react-dom@latest --dev

chuck
303 声望41 粉丝