web-dev-qa-db-fra.com

Utilisation de JS React dans TypeScript: le type d'élément JSX 'MyComponent' n'est pas une fonction constructeur pour les éléments JSX

Je travaille avec un projet hérité JavaScript qui utilise React framework. Nous avons là quelques-uns React composant défini que je voudrais réutiliser dans un TypeScript totalement différent React.

Le composant JS React est défini dans le fichier controls.jsx et se présente comme suit:

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

Dans mon projet TypeScript React project j'essaie de l'utiliser comme ça:

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

mais j'obtiens l'erreur suivante: enter image description here

Les messages d'erreur indiquent:

Le type d'élément JSX 'MyComponent' n'est pas une fonction constructeur pour les éléments JSX. Le type 'MyComponent' ne contient pas les propriétés suivantes du type 'ElementClass': context, setState, forceUpdate, props et 2 more.ts (2605) La classe d'élément JSX ne prend pas en charge les attributs car elle n'a pas de propriété 'props' .ts (2607)

J'ai déjà essayé la solution avec un fichier de typage personnalisé décrit dans la question this , mais cela ne change rien.

Je comprends que le composant JS a des propriétés fortement typées requises par TypeScript manquantes, mais dans mon tsconfig.json, j'ai allowJs défini sur vrai:

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

alors j'espérais que ça marcherait ...

Merci de votre aide

8
Dawid Sibiński

Vous devez transformer allowSyntheticDefaultImports en true car React n'exporte pas par défaut.

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

Vous devez également ajouter la forme des accessoires de composant. Par exemple, dans votre code:

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

Selon moi, vous avez deux options:

  1. Vous pouvez fournir un fichier de déclarations dans le même répertoire que controls.jsx nommé controls.t.ds.

Cette option a deux façons de procéder. Le plus simple est d'exporter une fonction avec le nom de MyComponent:

export function MyComponent(): any;

La seconde consiste à exporter une variable qui imite la structure de classe d'un composant:

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. Votre deuxième option consiste à importer votre composant et à le réexporter avec une définition de type. Ce fichier serait inclus quelque part dans le projet:
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};

Et puis dans votre fichier qui souhaite utiliser MyComponent, vous devez l'importer à partir de ce nouveau fichier à la place.

Ce sont toutes des solutions vraiment grossières, mais je crois que c'est la seule solution car TypeScript ne vous permet pas de définir un module relatif à votre projet:

declare module "../project/Controls" {

}

J'espère que cela t'aides.

0
Kyle

Voici les configs pour vous référence que j'utilise dans mon exemple d'application.

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

Je n'ai pas utilisé de webpack.

0
Deep