Que signifie l'erreur «Le type d'élément JSX '…' n'a pas de construction ou de signature d'appel»?

158

J'ai écrit du code:

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

J'obtiens une erreur:

Le type d'élément JSX Elemn'a aucune construction ni signature d'appel

Qu'est-ce que ça veut dire?

Ryan Cavanaugh
la source

Réponses:

196

Il s'agit d'une confusion entre les constructeurs et les instances .

N'oubliez pas que lorsque vous écrivez un composant dans React:

class Greeter extends React.Component<any, any> {
    render() {
        return <div>Hello, {this.props.whoToGreet}</div>;
    }
}

Vous l'utilisez de cette façon:

return <Greeter whoToGreet='world' />;

Vous ne l' utilisez pas de cette façon:

let Greet = new Greeter();
return <Greet whoToGreet='world' />;

Dans le premier exemple, nous transmettons Greeterla fonction constructeur de notre composant. C'est l'usage correct. Dans le deuxième exemple, nous transmettons une instance de Greeter. C'est incorrect et échouera au moment de l'exécution avec une erreur du type "L'objet n'est pas une fonction".


Le problème avec ce code

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

est qu'il attend une instance de React.Component. Ce que vous voulez une fonction qui prend un constructeur pour React.Component:

function renderGreeting(Elem: new() => React.Component<any, any>) {
    return <span>Hello, <Elem />!</span>;
}

ou de manière similaire:

function renderGreeting(Elem: typeof React.Component) {
    return <span>Hello, <Elem />!</span>;
}
Ryan Cavanaugh
la source
31
Au moment d'écrire ces lignes, si vous les utilisez @types/react, il est plus facile à utiliserfunction RenderGreeting(Elem: React.ComponentType) { ... }
Jthorpe
Juste curieux, comment écrit-on function renderGreeting(Elem: typeof React.Component)dans ES6?
Bruce Sun
Merci. Quel serait le cas si nous voulions passer un composant fonctionnel sans état? Je suppose que function renderGreeting (Elem: new() => React.SFC<any>){...}si oui, pourquoi déclarons-nous le type de SFC comme ceci:, const Hello:React.SFC<any> = (props) => <div>Hello World</div>et non:const Hello: new() =>React.SFC<any> = (props) => <div>Hello World</div>
Ben Carp
1
@Jthorpe votre commentaire devrait être une réponse et une réponse acceptée à cette OMI.
Tomáš Hübelbauer
export const BackNavigationTextWrapper = (WrappedComponent: typeof React.Component) => { const BackNavigationTextWrappedComponent = (props, { commonElements = {} }: any) => { return <WrappedComponent {...props} backLabel={commonElements.backLabel || 'Go back to reservation details'} /> }; BackNavigationTextWrappedComponent.type = WrappedComponent.type; return BackNavigationTextWrappedComponent; }; J'obtiens une erreur "La propriété 'type' n'existe pas sur le type 'typeof Component'".
Prakash P
60

Si vous souhaitez prendre une classe de composant comme paramètre (par rapport à une instance), utilisez React.ComponentClass:

function renderGreeting(Elem: React.ComponentClass<any>) {
    return <span>Hello, <Elem />!</span>;
}
Luke
la source
Maintenant, avec des composants fonctionnels dans le mélange, vous devriez probablement utiliser le React.ComponentType<any>type à la place pour les inclure.
Zack le
34

Lorsque je convertis de JSX en TSX et que nous conservons certaines bibliothèques sous le nom js / jsx et convertissons d'autres en ts / tsx, j'oublie presque toujours de changer les instructions d'importation js / jsx dans les fichiers TSX \ TS de

import * as ComponentName from "ComponentName";

à

import ComponentName from "ComponentName";

Si vous appelez un ancien composant de style JSX (React.createClass) depuis TSX, utilisez

var ComponentName = require("ComponentName")

Michael
la source
4
Je pense que tu voulais dire le contraire?
apieceofbart
5
La modification de l'instruction inport n'est pas nécessaire si vous configurez TypeScript (ie in tsconfig.json) sur allowSyntheticDefaultImports. Voir: typescriptlang.org/docs/handbook/compiler-options.html et discussion ici: blog.jonasbandi.net/2016/10/…
jbandi
11

Si vous ne vous souciez vraiment pas des accessoires, le type le plus large possible est React.ReactType.

Cela permettrait de passer des éléments dom natifs sous forme de chaîne. React.ReactTypecouvre tous ces éléments:

renderGreeting('button');
renderGreeting(() => 'Hello, World!');
renderGreeting(class Foo extends React.Component {
   render() {
      return 'Hello, World!'
   }
});
epsilon
la source
8

Si vous utilisez material-ui , accédez à la définition de type du composant, qui est soulignée par TypeScript. Très probablement, vous verrez quelque chose comme ça

export { default } from './ComponentName';

Vous avez 2 options pour résoudre l'erreur:

1.Ajouter .defaultlors de l'utilisation du composant dans JSX:

import ComponentName from './ComponentName'

const Component = () => <ComponentName.default />

2.Renommez le composant, qui est exporté comme "par défaut", lors de l'importation:

import { default as ComponentName } from './ComponentName'

const Component = () => <ComponentName />

De cette façon, vous n'avez pas besoin de spécifier .defaultchaque fois que vous utilisez le composant.

Eduard
la source
1
Cela m'a aidé à résoudre mon problème.
WhiteWalker
2

Dans mon cas, j'utilisais React.ReactNodecomme type pour un composant fonctionnel au lieu de React.FCtype.

Dans ce composant pour être 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>
      }
    />
  )
}

ArchNoob
la source
2

Comme @Jthorpe y a fait allusion, ComponentClassn'autorise que l'un Componentou l' autre PureComponentmais pas un FunctionComponent.

Si vous essayez de passer un FunctionComponent, dactylographié générera une erreur similaire à ...

Type '(props: myProps) => Element' provides no match for the signature 'new (props: myProps, context?: any): Component<myProps, any, any>'.

Cependant, en utilisant ComponentTypeplutôt que ComponentClassvous autorisez les deux cas. Selon le fichier de déclaration de réaction, le type est défini comme ...

type ComponentType<P = {}> = ComponentClass<P, any> | FunctionComponent<P>
Nickofthyme
la source
2

Dans mon cas, je manquais newdans la définition de type.

some-js-component.d.ts fichier:

import * as React from "react";

export default class SomeJSXComponent extends React.Component<any, any> {
    new (props: any, context?: any)
}

et dans le tsxfichier où j'essayais d'importer le composant non typé:

import SomeJSXComponent from 'some-js-component'

const NewComp = ({ asdf }: NewProps) => <SomeJSXComponent withProps={asdf} />
Jmunsch
la source
2

Lors de la déclaration du composant React Class, utilisez React.ComponentClass au lieu de React.Componentpuis cela corrigera l'erreur ts.

snowyBunny
la source
1

Vous pouvez utiliser

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

Cependant, est-ce que ce qui suit fonctionne?

function renderGreeting(Elem: React.ComponentType) {
    const propsToPass = {one: 1, two: 2};

    return <span>Hello, <Elem {...propsToPass} />!</span>;
}
Mark Kalinovits
la source