¿Qué significa el error "El tipo de elemento JSX '...' no tiene ninguna construcción o firma de llamada"?

158

Escribí un código:

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

Recibo un error:

El tipo de elemento JSX Elemno tiene ninguna construcción o firma de llamada

Qué significa eso?

Ryan Cavanaugh
fuente

Respuestas:

196

Esta es una confusión entre constructores e instancias .

Recuerde que cuando escribe un componente en React:

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

Lo usas de esta manera:

return <Greeter whoToGreet='world' />;

No lo usas de esta manera:

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

En el primer ejemplo, estamos pasando Greeter, la función constructora de nuestro componente. Ese es el uso correcto. En el segundo ejemplo, estamos pasando una instancia de Greeter. Eso es incorrecto y fallará en tiempo de ejecución con un error como "El objeto no es una función".


El problema con este código

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

es que está esperando una instancia de React.Component. Para qué quieres una función que tome un constructor para React.Component:

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

o similarmente:

function renderGreeting(Elem: typeof React.Component) {
    return <span>Hello, <Elem />!</span>;
}
Ryan Cavanaugh
fuente
31
Al momento de escribir esto, si los está usando @types/react, es más fácil de usarfunction RenderGreeting(Elem: React.ComponentType) { ... }
Jthorpe
Por curiosidad, ¿cómo escribimos function renderGreeting(Elem: typeof React.Component)en ES6?
Bruce Sun
Gracias. ¿Cuál sería el caso si queremos pasar un componente funcional sin estado? Supongo que function renderGreeting (Elem: new() => React.SFC<any>){...}si es así, ¿por qué declaramos el tipo de SFC como este: const Hello:React.SFC<any> = (props) => <div>Hello World</div>y no:const Hello: new() =>React.SFC<any> = (props) => <div>Hello World</div>
Ben Carp
1
@Jthorpe su comentario debe una respuesta y una aceptada en esa 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; }; Recibo un error "La propiedad 'tipo' no existe en el tipo 'tipo de componente'".
Prakash P
60

Si desea tomar una clase de componente como parámetro (frente a una instancia), use React.ComponentClass:

function renderGreeting(Elem: React.ComponentClass<any>) {
    return <span>Hello, <Elem />!</span>;
}
Luke
fuente
Ahora con componentes funcionales en la mezcla, probablemente debería usar el React.ComponentType<any>tipo en su lugar para incluirlos.
Zack
34

Cuando convierto de JSX a TSX y mantenemos algunas bibliotecas como js / jsx y convierto otras a ts / tsx, casi siempre olvido cambiar las declaraciones de importación js / jsx en los archivos TSX \ TS de

import * as ComponentName from "ComponentName";

a

import ComponentName from "ComponentName";

Si llama a un antiguo componente de estilo JSX (React.createClass) desde TSX, utilice

var ComponentName = require("ComponentName")

Miguel
fuente
44
Creo que te referías al revés?
apieceofbart
55
No es necesario cambiar la declaración de entrada si configura TypeScript (es decir, en tsconfig.json) allowSyntheticDefaultImports. Ver: typescriptlang.org/docs/handbook/compiler-options.html y discusión aquí: blog.jonasbandi.net/2016/10/…
jbandi
11

Si realmente no te interesan los accesorios, entonces el tipo más amplio posible es React.ReactType.

Esto permitiría pasar elementos dom nativos como una cadena. React.ReactTypecubre todo esto:

renderGreeting('button');
renderGreeting(() => 'Hello, World!');
renderGreeting(class Foo extends React.Component {
   render() {
      return 'Hello, World!'
   }
});
épsilon
fuente
8

Si está utilizando material-ui , vaya a la definición de tipo del componente, que TypeScript subraya. Lo más probable es que veas algo como esto

export { default } from './ComponentName';

Tiene 2 opciones para resolver el error:

1.Agregue .defaultcuando use el componente en JSX:

import ComponentName from './ComponentName'

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

2. Cambie el nombre del componente, que se está exportando como "predeterminado", al importar:

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

const Component = () => <ComponentName />

De esta manera, no necesita especificar .defaultcada vez que usa el componente.

Eduard
fuente
1
Esto me ayudó a solucionar mi problema.
WhiteWalker
2

En mi caso, estaba usando React.ReactNodecomo tipo para un componente funcional en lugar de React.FCtipo.

En este componente para ser exactos:

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
fuente
2

Como aludió @Jthorpe, ComponentClasssolo permite cualquiera Componento PureComponentno pero a FunctionComponent.

Si intenta pasar un FunctionComponent, mecanografiado arrojará un error similar a ...

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

Sin embargo, al utilizar en ComponentTypelugar de ComponentClasspermitir ambos casos. Según el archivo de declaración de reacción, el tipo se define como ...

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

En mi caso me faltaba newla definición de tipo.

some-js-component.d.ts expediente:

import * as React from "react";

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

y dentro del tsxarchivo donde estaba tratando de importar el componente sin tipo:

import SomeJSXComponent from 'some-js-component'

const NewComp = ({ asdf }: NewProps) => <SomeJSXComponent withProps={asdf} />
jmunsch
fuente
2

Al declarar el componente React Class, use en React.ComponentClass lugar de React.Componententonces solucionará el error ts.

nevadoConejito
fuente
1

Puedes usar

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

Sin embargo, ¿funciona lo siguiente?

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

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