Reputation: 1717
I'm working with a JavaScript legacy project which uses React framework. We have there some React component defined which I'd like to re-use in a totally different TypeScript React project.
The JS React component is defined in controls.jsx file and looks as follows:
export class MyComponent extends React.Component {
render() {
return <h1>Hi from MyComponent! Message provided: {this.props.message}</h1>;
}
}
In my TypeScript React project I'm trying to use it like that:
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")
);
but I'm getting the following error:
Error messages say:
JSX element type 'MyComponent' is not a constructor function for JSX elements. Type 'MyComponent' is missing the following properties from type 'ElementClass': context, setState, forceUpdate, props, and 2 more.ts(2605) JSX element class does not support attributes because it does not have a 'props' property.ts(2607)
I've already tried the solution with custom typings file described in this question, but it changes nothing.
I understand that the JS component has some strongly-typed properties required by TypeScript missing, but in my tsconfig.json I have the allowJs set to 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"
]
}
so I hoped it should work...
Thanks for your help
Upvotes: 11
Views: 18406
Reputation: 1325
You should turn allowSyntheticDefaultImports
to true
since React does not export default.
(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"
]
}
Also, you should add the shape for component props. For example, in your code:
export class MyComponent extends React.Component<{ message: string }> { ...
Upvotes: 2
Reputation: 4014
The way I see it, you have two options:
controls.jsx
named controls.t.ds
.This options has two ways of going about it. The easiest is just to export a function with the name of MyComponent
:
export function MyComponent(): any;
The second is to export a variable that mimics the class structure of a component:
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};
And then in your file that wants to use MyComponent
, you would import it from that new file instead.
These are all really gross solutions, but I believe it's the only solution as TypeScript doesn't let you define a module that is relative to your project:
declare module "../project/Controls" {
}
Hope this helps.
Upvotes: 0
Reputation: 9794
Here are the configs for you reference which i am using in my sample 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"
]
}
}
I have not used webpack.
Upvotes: 0