Uso del componente JS React en TypeScript: el tipo de elemento JSX 'MyComponent' no es una función constructora para elementos JSX

8

Estoy trabajando con un proyecto heredado de JavaScript que usa el marco React. Tenemos definido algún componente React que me gustaría reutilizar en un proyecto TypeScript React totalmente diferente.

El componente JS React se define en el archivo controls.jsx y tiene el siguiente aspecto:

export class MyComponent extends React.Component {
  render() {
    return <h1>Hi from MyComponent! Message provided: {this.props.message}</h1>;
  }
}

En mi proyecto TypeScript React estoy tratando de usarlo así:

import * as React from "react";
import * as ReactDOM from "react-dom";
import { MyComponent } from "../JavaScriptProject/controls";

ReactDOM.render(
  <MyComponent message="some nice message"/>,
    document.getElementById("documents-tree")
);

pero recibo el siguiente error: ingrese la descripción de la imagen aquí

Los mensajes de error dicen:

El tipo de elemento JSX 'MyComponent' no es una función constructora para elementos JSX. Al tipo 'MyComponent' le faltan las siguientes propiedades del tipo 'ElementClass': context, setState, forceUpdate, props y 2 more.ts (2605) La clase de elemento JSX no admite atributos porque no tiene una propiedad 'props'. (2607)

Ya probé la solución con el archivo de tipificación personalizado descrito en esta pregunta, pero no cambia nada.

Entiendo que el componente JS tiene algunas propiedades fuertemente tipadas requeridas por falta de TypeScript, pero en mi tsconfig.json tengo los allowJs establecidos en verdadero :

{
  "compilerOptions": {
    "allowJs": true,
    "baseUrl": ".",
    "experimentalDecorators": true,
    "jsx": "react",
    "noEmitOnError": true,
    "outDir": "lib",
    "sourceMap": true,
    "target": "es5",
    "lib": [
      "es2015",
      "dom"
    ]
  },
  "exclude": [
    "node_modules",
    "dist",
    "lib",
    "lib-amd"
  ]
}

así que esperaba que funcionara ...

Gracias por tu ayuda

Dawid Sibiński
fuente
Lo intenté, pero nada ayuda ... No uso hilo, así que pocos de ellos eran irrelevantes. Los demás no ayudan en absoluto ...
Dawid Sibiński
Funciona bien para mí sin ningún problema. Acabo de crear una aplicación de mecanografía y agregué el archivo controls.jsx en src.
Profundo
Hmm ... ¿entonces tal vez es una cuestión de algunos paquetes npm? O sus versiones? ¿Podría compartir su tsconfig.json, package.json y quizás webpack.config.js?
Dawid Sibiński
Esto suena mucho como si tuvieras reacciones de reacción y reacciones no coincidentes. ¿Puedes compartir tu paquete-lock & package.json? Es posible que deba colocar el bloqueo de paquete en pastebin / gist debido a su longitud.
Dan Pantry

Respuestas:

1

Deberías recurrir allowSyntheticDefaultImportsatrue desde Reaccionar no exporta de forma predeterminada.

(fuente: https://github.com/facebook/react/blob/master/packages/react/index.js )

{
  "compilerOptions": {
    "allowJs": true,
    "baseUrl": ".",
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true, // turn on allowSyntheticDefaultImports
    "jsx": "react",
    "noEmitOnError": true,
    "outDir": "lib",
    "sourceMap": true,
    "target": "es5",
    "lib": [
      "es2015",
      "dom"
    ]
  },
  "exclude": [
    "node_modules",
    "dist",
    "lib",
    "lib-amd"
  ]
}

Además, debe agregar la forma para los accesorios de los componentes. Por ejemplo, en su código:

export class MyComponent extends React.Component<{ message: string }> { ...
Long Nguyen
fuente
1
Gracias por responder Ajuste allowSyntheticDefaultImportsde truelos cambios nada, todavía estoy consiguiendo el mismo error. No puedo agregar forma para accesorios de componentes, porque está definido en un archivo .jsx y obtengo el siguiente error: "'argumentos de tipo' solo se pueden usar en un archivo .ts.ts (8011)"
Dawid Sibiński
0

Aquí están las configuraciones para su referencia que estoy usando en mi aplicación de muestra.

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react"
  },
  "include": [
    "src"
  ]
}

package.json

{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.4.0",
    "@testing-library/user-event": "^7.1.2",
    "@types/jest": "^24.0.23",
    "@types/node": "^12.12.18",
    "@types/react": "^16.9.16",
    "@types/react-dom": "^16.9.4",
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
    "react-scripts": "3.3.0",
    "typescript": "^3.7.3"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

No he usado webpack.

Profundo
fuente
Incluso si tomo sus configuraciones no ayuda ...: /
Dawid Sibiński
0

A mi modo de ver, tienes dos opciones:

  1. Puede proporcionar un archivo de declaraciones en el mismo directorio con el controls.jsxnombre controls.t.ds.

Esta opción tiene dos formas de hacerlo. Lo más fácil es exportar una función con el nombre de MyComponent:

export function MyComponent(): any;

El segundo es exportar una variable que imita la estructura de clases de un componente:

interface Element {
    render(): any;
    context: any;
    setState: any;
    forceUpdate: any;
    props: any;
    state: any;
    refs: any;
}

interface MyComponent extends Element {}

export var MyComponent: {
    new(): MyComponent;
}
  1. Su segunda opción es importar su componente y volver a exportarlo con una definición de tipo. Este archivo se incluiría en algún lugar dentro del proyecto:
import React from 'react';
import {MyComponent as _MyComponent} from '../project-a/Controls';

interface MyComponent extends React.Component {}

const MyComponent = (_MyComponent as any) as {
    new(): MyComponent;
};

export {MyComponent};

Y luego, en su archivo que quiere usar MyComponent, lo importaría de ese nuevo archivo.

Todas estas son soluciones realmente groseras, pero creo que es la única solución, ya que TypeScript no le permite definir un módulo que sea relativo a su proyecto:

declare module "../project/Controls" {

}

Espero que esto ayude.

Kyle
fuente
Probé la opción 2 y declaró este archivo adicional, agregando adicionalmente un tipo messagede stringtipo al componente ( MyComponent extends React.Component<{ message: string }>y VS Code no me da ningún error al importar este componente desde el nuevo archivo. Sin embargo, todavía recibo un error de compilación en el nuevo archivo:Module not found: Error: Can't resolve '../../../../JS_TS_Project/Scripts/application/controls'
Dawid Sibiński
@ DawidSibiński ¿Qué empaquetador está utilizando? Metro, babel, WebPack?
Kyle
Estoy usando webpack
Dawid Sibiński
@ DawidSibiński Creo que tendrá que decirle a Webpack que incluya el JS_TS_Project en su resolución de inclusión. Vea esta respuesta SO: stackoverflow.com/a/54257787/701263
Kyle
Gracias @Kyle, pero todavía no puedo hacer que funcione ... Me di por vencido un poco, porque finalmente, usé un componente TS separado. Sin embargo, el problema sigue siendo abierto e interesante. Incluso si una de estas soluciones funcionó, sigue siendo un acoplamiento estrecho de ambos módulos / proyectos y está lejos de ser una solución confiable y reutilizable.
Dawid Sibiński