Bohulenkov Artem Bohulenkov Artem - 4 months ago 8
React JSX Question

React 'Invariant Violation' in typescript application

I am getting the following error when trying to use react-collapse (repository) component in my typescript application with self-made d.ts file:


Warning: React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components).


and


Uncaught Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.


index.d.ts:

declare namespace ReactCollapse {
export class Collapse extends __React.Component<any, any> { }
}
declare module "react-collapse" {
export = ReactCollapse;
}


Original index.js from react-collapse:

'use strict';
const Collapse = require('./Collapse').default;
module.exports = Collapse;


Application code:

import { Collapse } from 'react-collapse';
const render = () => {
ReactDOM.render(
<Collapse isOpened={true}>One</Collapse>,
document.getElementById('test-container')
);
}
render();


I am using typescript 1.8.10, redux-collapse was installed via npm and tsc uses commonjs generation. Application is built using webpack. I am pretty sure, that I have mistakes in my .d.ts file, but I just can't figure it out.

UPDATE 1:

Tried to add render method to typing like Hunter suggested, so typing now looks like this:

declare namespace ReactCollapse {
export class Collapse extends __React.Component<any, any> {
public render(): JSX.Element;
}
}
declare module "react-collapse" {
export = ReactCollapse;
}


But I am still getting the same error.

UPDATE 2:

Somehow, I managed to fix error with next typing and module resolution call:

index.d.ts:

declare class Collapse extends __React.Component<any, any> {}

declare module "react-collapse" {
export = Collapse;
}


Code:

import Collapse = require('react-collapse');
const render = () => {
ReactDOM.render(
<Collapse isOpened={true}>One</Collapse>,
document.getElementById('test-container')
);
}
render();


But, the thing is - it was a random guess, and I don't understand why it works. So, maybe anyone can explain it ?

Answer

Firstly, in the index.js we can see that the original index.js exports just the class as the export value, not any wrapper around it.

In the first version of the code, you declare that the value exported is the namespace (so, a wrapper, containing the class inside), so the import { Collapse } from 'react-collapse' expects to get smth like { Collapse: { /* the component class here*/ } }, but in reality gets just the inner { /* the component class here*/ }. Then you try to use a prop on it named Collapse (which is undefined) and pass it to the React, getting the error.

The second version declares (correctly) that the export value is the class itself, and imports it directly, without the { xxx } decomposing syntax, so - no surprise - it works.