TypeScript: TS files cannot use JSX components declared as a classes in JS/JSX file

CC @donataswix

It appears that using a React component written as a class in a .js file causes some issues.

Start a project with the following files:

tsconfig.json

{
  "compilerOptions": {
    "module": "es2015",
    "target": "esnext",
    "jsx": "react-native",
    "strictNullChecks": true,
    "allowSyntheticDefaultImports": true,
    "allowJs": true,
    "lib": [
      "es5",
      "es6",
      "es7",
      "es2017",
      "dom"
    ],
    "outDir": "dist"
  },
  "include": [
    "**/*.ts",
    "**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

index.tsx

import React from 'react';
import Hello from './hello';

class A extends React.Component<void,void> {
  render() {
    return (
      <div>
        <Hello/>
      </div>
    );
  }
}

hello.js

import React from 'react';

export default class Hello extends React.Component {
  render() {
    return <div>Hello</div>;
  }
}

Expected: No problems Actual: Error on <Hello />: JSX element type ‘Hello’ does not have any construct or call signatures.

Note that if you add type arguments to Component in hello.js, this will actually fix the problem (while creating new ones).

Potentially related is #13609.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 1
  • Comments: 38 (13 by maintainers)

Commits related to this issue

Most upvoted comments

Helps for me

//this is tsx file

import React, { Component } from "react";
import SomeComponent from './SomeComponent.js';

/* tslint:disable */
const JsSomeComponent: any = SomeComponent; //todo delete me after refactoring to TS
/* tslint:enable */

class MyTSXComponent extends Component {
    public render() {
        return <JsSomeComponent/>;
    }
}

Can we please reopen it? It doesn’t work if component have any props inside and it’s a show stopper for React projects which are migrating to TypeScript

+1 I have the same problem guys

This is killing me trying to migrate a large project to Typescript. I’ve got a hundred plain ol React components and I’d love to be able to do 1 at a time, but once converted to .tsx, they can’t import any other .js files without this error.

So we know the root cause of this issue - React’s type definitions have their default Props and State types set to {} - the intention being that this gives you safety; if you specify the generics, then you can access props and state safely, otherwise you get no props or state (other than what you may inherit from intrinsics). The downside to this is that in JS, you often don’t or can’t specify the generic arguments - so they default to {} (whereas previous they defaulted to any), and you can’t do anything with the props or state without an error. In short, React’s types used to default open, but now default closed. This behavior is arguably better for TS users, but is without a doubt a showstopper for JS users relying gradual typing with the same types. We’re trying to come up with a solution that can satisfy both user-bases, but it’s difficult.

While we work on it, a workaround in the meantime is editing the react.d.ts to use any for its’ default props and state types (the replacement would be this: interface Component<P = any, S = any> extends ComponentLifecycle<P, S> { }), or adding JSDoc arguments to your JS components to specify their props and/or state types (which you should consider preferred if it is easy, as when you migrate the file to TS we provide a bunch of quick fixes to translate that JSDoc into proper TS code), for example:

import React, { Component } from "react";

/**
 * @augments {Component<{location: string}, *>}
 */
class MyComponent extends Component {
    render() {
        return <h1>Hello World from {this.props.location}</h1>
    }
}

const x = <MyComponent location="Redmond" />

sadly also suffering from this issue – is a huge deal when trying to migrate incrementally to typescript.

I ran into this as well… while using react-select with latest typings. Has this issue been fixed in TS 2.6.2?

This works for me:

//this is tsx file

import React, { Component } from "react";

const SomeComponent = require('./SomeComponent.js');

/* tslint:disable */
const JsSomeComponent: any = SomeComponent; //todo delete me after refactoring to TS
/* tslint:enable */

class MyTSXComponent extends Component {
    public render() {
        return <JsSomeComponent/>;
    }
}

We have a fix on the compiler side in https://github.com/Microsoft/TypeScript/pull/19977 to accommodate the react changes. Can you give tomorrow’s typescript@next a try?

@mhegazy any update here? I’m in a similar boat to @z0d14c in which this makes adopting ts in a large codebase very difficult.

@weswigham can you take a look here. unfortunately we can not remove the defaults from react definition file as that would be a big breaking change, but what can we do not make sure this scenario still works.