Search code examples
javascriptreactjstypescriptreact-grid-layout

Typescript definition for a module that is a function and has namespaces?


I am trying to come up with a *.d.ts file for react-grid-layout. As its index.js file shows it exports a function - a subclass of React.Component called ReactGridLayout:

// react-grid-layout/index.js
module.exports = require('./build/ReactGridLayout').default;
module.exports.utils = require('./build/utils');
// ...

Requiring:

var ReactGridLayout = require('react-grid-layout');
console.log(ReactGridLayout);
// --> function ReactGridLayout(props , context) { ...

It also exports some other stuff separated into namespaces:

for (var f in ReactGridLayout) {
  if (ReactGridLayout.hasOwnProperty(f)) {
    console.log(f);
  }
}
// --> utils
// ...

So it does a single export and multiple exports.

I tried the Single Complex Object in Modules approach described on the Typescript site and this declaration file on GitHub but without much success.

Update

Ignoring the other stuff for now my definition for ReactGridLayout looks like this:

// react-grid-layout.d.ts
declare module 'react-grid-layout' {

  import * as React from 'react';

  export default class ReactGridLayout<P,S> extends React.Component<P,S> {

    containerHeight():void;

    onWidthChange(width:number):void;

   /* more methods here ... */

}

Which compiles. However it generates Javascript like:

var react_grid_layout_1 = require('react-grid-layout');

React.createElement(react_grid_layout_1.default, null, ...

When it should be:

React.createElement(react_grid_layout_1, null, ...    

Solution

  • Because the react-grid-layout is exporting the ReactGridLayout class "directly" rather than as default you can't do a export default (you've seen what happens already). I think that you have to resolve to export = syntax, which however seems to limit you to exporting only one thing. One way to solve this is to make use of TypeScript's declaration merging. Export a class and a namespace with the same name. It is more or less what react-grid-layout is doing.

    main.tsx

    import * as React from 'react'
    import * as ReactGridLayout from 'react-grid-layout'
    
    var grid = new ReactGridLayout(null, null);
    var responsiveGrid = new ReactGridLayout.ResponsiveReactGridLayout(null, null);
    
    <ReactGridLayout></ReactGridLayout>
    //<ReactGridLayout.ResponsiveReactGridLayout></ReactGridLayout.ResponsiveReactGridLayout>
    

    main.js (transpiled)

    "use strict";
    var React = require('react');
    var ReactGridLayout = require('react-grid-layout');
    var grid = new ReactGridLayout(null, null);
    var responsiveGrid = new ReactGridLayout.ResponsiveReactGridLayout(null, null);
    React.createElement(ReactGridLayout, null);
    //<ReactGridLayout.ResponsiveReactGridLayout></ReactGridLayout.ResponsiveReactGridLayout>
    

    types.d.ts

    declare module 'react-grid-layout' {
    
        import * as React from 'react';
    
        class ReactGridLayout extends React.Component<ReactGridLayout.Props, ReactGridLayout.State> {
            // members
        }
    
        namespace ReactGridLayout {
            export interface State {
                activeDrag?: any; // declare LayoutItem, etc..
                // etc...
            }
    
            export interface Props {
                className?: string
                // etc...
            }
    
            export class ResponsiveReactGridLayout extends React.Component<any, any>
            {
                // etc
            }
        }
    
        export = ReactGridLayout;
    }
    

    BTW: You may find it easier to look at the ES6 code of react-grid-layout.