在 TypeScript 中使用 JS React 组件:JSX 元素类型“MyComponent”不是 JSX 元素的构造函数

Posted

技术标签:

【中文标题】在 TypeScript 中使用 JS React 组件:JSX 元素类型“MyComponent”不是 JSX 元素的构造函数【英文标题】:Using JS React component in TypeScript: JSX element type 'MyComponent' is not a constructor function for JSX elements 【发布时间】:2020-04-09 17:46:57 【问题描述】:

我正在处理一个使用 React 框架的 javascript 遗留项目。我们已经定义了一些 React 组件,我想在一个完全不同的 TypeScript React 项目中重用它们。

JS React 组件在 controls.jsx 文件中定义,如下所示:

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

在我的 TypeScript React 项目中,我尝试这样使用它:

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

但我收到以下错误:

错误信息说:

JSX 元素类型“MyComponent”不是 JSX 的构造函数 元素。类型“MyComponent”缺少以下属性 来自“ElementClass”类型:上下文、setState、forceUpdate、props 和 2 more.ts(2605) JSX 元素类不支持属性,因为它 没有“道具”属性。ts(2607)

我已经尝试了this 问题中描述的自定义类型文件的解决方案,但它没有任何改变。

我了解 JS 组件缺少 TypeScript 所需的一些强类型属性,但在我的 tsconfig.json 中,我将 allowJs 设置为 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"
  ]

所以我希望它应该工作......

感谢您的帮助

【问题讨论】:

你尝试过这些解决方案吗 -> github.com/DefinitelyTyped/DefinitelyTyped/issues/21242 ? 我试过了,但没有任何帮助......我不使用纱线,所以它们中的少数是无关紧要的。其他人根本没有帮助...... 它对我来说很好用,没有任何问题。我刚刚创建了一个打字稿应用程序并在 src 添加了 controls.jsx 文件。 嗯......所以也许这是一些 npm 包的问题?还是他们的版本?你能分享你的 tsconfig.json、package.json 和 webpack.config.js 吗? 这听起来很像你的 react 和 react-dom 类型不匹配。你能分享你的 package-lock 和 package.json 吗?由于它的长度,您可能需要在 pastebin/gist 上放置 package-lock。 【参考方案1】:

你应该把 allowSyntheticDefaultImports 变成 true 因为 React 不导出默认值。

(来源: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"
  ]

此外,您应该为组件道具添加形状。例如,在您的代码中:

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

【讨论】:

感谢您的回答。将allowSyntheticDefaultImports 设置为true 没有任何改变,我仍然遇到同样的错误。我无法为组件道具添加形状,因为它是在 .jsx 文件中定义的,并且出现以下错误:“'type arguments' can only be used in a .ts file.ts(8011)”【参考方案2】:

以下是我在示例应用程序中使用的配置供您参考。

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

我没用过 webpack。

【讨论】:

即使我接受你的配置也无济于事......:/【参考方案3】:

在我看来,你有两个选择:

    您可以在与controls.jsx 相同的目录中提供一个名为controls.t.ds 的声明文件。

此选项有两种处理方式。最简单的就是导出一个名为MyComponent的函数:

export function MyComponent(): any;

第二种是导出一个模仿组件类结构的变量:

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;

    您的第二个选项是导入您的组件并使用类型定义重新导出它。该文件将包含在项目中的某处:
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;

然后在你想要使用MyComponent 的文件中,你可以从那个新文件中导入它。

这些都是非常糟糕的解决方案,但我相信这是唯一的解决方案,因为 TypeScript 不允许您定义与您的项目相关的模块:

declare module "../project/Controls" 


希望这会有所帮助。

【讨论】:

我尝试了选项 2,并声明了这个额外的文件,另外添加了一个 string 类型的 message 到组件(MyComponent extends React.Component&lt; message: string &gt; 和 VS Code 在导入时没有给我任何错误这个组件来自新文件。但是,我仍然在新文件中收到编译错误:Module not found: Error: Can't resolve '../../../../JS_TS_Project/Scripts/application/controls' @DawidSibiński 您使用的是什么打包程序? Metro、babel、WebPack? 我正在使用 webpack @DawidSibiński 我相信你必须告诉 Webpack 将 JS_TS_Project 包含在它的包含分辨率中。看到这个答案:***.com/a/54257787/701263 感谢@Kyle,但仍然无法让它工作......我放弃了一点,因为最后,我使用了一个单独的 TS 组件。然而,这个问题仍然是开放和有趣的。即使其中一个解决方案有效,这仍然是两个模块/项目的紧密耦合,远非可靠且可重用的解决方案。

以上是关于在 TypeScript 中使用 JS React 组件:JSX 元素类型“MyComponent”不是 JSX 元素的构造函数的主要内容,如果未能解决你的问题,请参考以下文章

如何在带有 typescript 项目的 react js 中使用 .env 文件?

如何在 React 应用程序的 typescript 文件中导入 js 模块(使用绝对路径)?

在 Typescript、Webpack、React 项目中使用 css-in-js 时没有重载匹配此调用错误

在 TypeScript 中使用 NextJS 设置 Jest + React 测试库 -- 设置 upp jest.config.js 时出错

在 TypeScript 中使用 JS React 组件:JSX 元素类型“MyComponent”不是 JSX 元素的构造函数

在 React TypeScript 组件中导入 Javascript