Usando o componente JS React no TypeScript: O tipo de elemento JSX 'MyComponent' não é uma função construtora para elementos JSX


8

Estou trabalhando com um projeto legado JavaScript que usa a estrutura React. Temos lá algum componente React definido que eu gostaria de reutilizar em um projeto TypeScript React totalmente diferente.

O componente JS React é definido no arquivo controls.jsx e tem a seguinte aparência:

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

No meu projeto TypeScript React, estou tentando usá-lo assim:

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

mas estou recebendo o seguinte erro: insira a descrição da imagem aqui

As mensagens de erro dizem:

O tipo de elemento JSX 'MyComponent' não é uma função construtora para elementos JSX. O tipo 'MyComponent' não possui as seguintes propriedades do tipo 'ElementClass': context, setState, forceUpdate, props e 2 more.ts (2605) A classe de elemento JSX não suporta atributos porque não possui uma propriedade 'props'. (2607)

Eu já tentei a solução com o arquivo de digitações personalizadas descrito nesta pergunta, mas isso não muda nada.

Eu entendo que o componente JS tem algumas propriedades de tipo forte exigidas pelo TypeScript ausentes, mas no meu tsconfig.json tenho o allowJs definido como true :

{
  "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"
  ]
}

então eu esperava que funcionasse ...

Obrigado pela ajuda



Eu tentei, mas nada ajuda ... Eu não uso fios, então poucos deles eram irrelevantes. Os outros não ajudam em nada ...
Dawid Sibiński

Funciona bem para mim sem nenhum problema. Acabei de criar um aplicativo datilografado e adicionei o arquivo controls.jsx no src.
Profundo

Hmm ... então talvez seja uma questão de alguns pacotes npm? Ou suas versões? Você poderia compartilhar seu tsconfig.json, package.json e talvez webpack.config.js?
Dawid Sibiński

isso parece muito com você ter tipos de reação e de dominação incompatíveis. você pode compartilhar seu package-lock & package.json? pode ser necessário colocar o bloqueio do pacote no pastebin / gist devido ao seu comprimento.
Dan Pantry,

Respostas:


1

Você deve ligar allowSyntheticDefaultImportspara truedesde Reagir não exporta padrão.

(fonte: 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"
  ]
}

Além disso, você deve adicionar a forma para adereços de componentes. Por exemplo, no seu código:

export class MyComponent extends React.Component<{ message: string }> { ...

1
Obrigado por responder. Definindo allowSyntheticDefaultImportsparatrue nada muda, ainda estou recebendo o mesmo erro. Não consigo adicionar forma aos acessórios de componentes, porque ele está definido em um arquivo .jsx e estou recebendo o seguinte erro: "'argumentos de tipo' podem ser usados ​​apenas em um arquivo .ts file.ts (8011)"
Dawid Sibiński

0

Aqui estão as configurações para você referência que eu estou usando no meu aplicativo de exemplo.

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"
    ]
  }
}

Eu não usei o webpack.


Mesmo se eu pegar suas configurações, isso não ajuda ...: /
Dawid Sibiński

0

Na minha opinião, você tem duas opções:

  1. Você pode fornecer um arquivo de declarações no mesmo diretório que o controls.jsxnomeadocontrols.t.ds .

Esta opção tem duas maneiras de fazer isso. O mais fácil é apenas exportar uma função com o nome de MyComponent:

export function MyComponent(): any;

O segundo é exportar uma variável que imita a estrutura de classe de um 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. Sua segunda opção é importar seu componente e reexportá-lo com uma definição de tipo. Este arquivo seria incluído em algum lugar do projeto:
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};

E então no seu arquivo que deseja usar MyComponent , você o importaria desse novo arquivo.

Essas são soluções realmente grosseiras, mas acredito que seja a única solução, pois o TypeScript não permite definir um módulo que seja relativo ao seu projeto:

declare module "../project/Controls" {

}

Espero que isto ajude.


Eu tentei a opção 2 e declarei esse arquivo extra, adicionalmente a adição de um messagede stringtipo para o componente ( MyComponent extends React.Component<{ message: string }>e Código VS não me dá nenhum erro ao importar este componente do novo arquivo. No entanto, ainda estou recebendo um erro de compilação no novo arquivo:Module not found: Error: Can't resolve '../../../../JS_TS_Project/Scripts/application/controls'
Dawid Sibiński 31/12/19

@ DawidSibiński Qual empacotador você está usando? Metro, babel, WebPack?
Kyle

Estou usando o webpack
Dawid Sibiński

@ DawidSibiński Eu acredito que você precisará informar o Webpack para incluir o JS_TS_Project na resolução de inclusão. Veja esta resposta do SO: stackoverflow.com/a/54257787/701263
Kyle

Obrigado @Kyle, mas ainda não consigo fazer funcionar ... Desisti um pouco, porque finalmente usei um componente TS separado. No entanto, a questão ainda permanece aberta e interessante. Mesmo que uma dessas soluções funcione, isso ainda está atrelando os módulos / projetos e está longe de ser uma solução confiável e reutilizável.
Dawid Sibiński
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.