12

I'm trying to convert a project to TypeScript, and I have most things set up, but I'm still struggling with getting the last few pieces put together. I want to be able to use this TypesScript project from a normal JavaScript project, so my understanding is that I need to emit d.ts files for my existing sources. My sources are all .js currently and we plan to slowly migrate to TS over time. My issue is getting the declarations to emit with the current exports/require statements.

A simple demonstration of the issue:

mod1.js

class MyClass {
    constructor(name) {
        this.name = name;
    }
}

module.exports = {
    MyClass,
};

mod2.js

const mod1 = require('./mod1');

module.exports = {
    MyClass: mod1.MyClass,
};

As soon as I try to export MyClass in mod2 in order to re-route the namespace one can access MyClass from when consuming the project, I get Declaration emit for this file requires using private name 'MyClass' from module '"mod1"'. An explicit type annotation may unblock declaration emit.ts(9006)

We have a lot of re-routes in our code base, groups of files that hold various classes, and then we use index.js files at each dir level to define which items are available in that namespace, and sometimes a bunch of UI elements which are instantiated class instances so we can make calls such as:

const {app, appui} = require('our-cool-app');
app.feature1.doSomething();
appui.component.someButton.click();

Is there an easy fix to get our d.ts files auto generated from the .js sources?

iceblueorbitz
  • 920
  • 1
  • 7
  • 17

2 Answers2

9

I solved this for a similar by adding a JSDoc @type comment above the offending thing. In my case I had a class that had a getter which returned a different class and any usage of the getter would give this error. YMMV.

So this...

// File Foo.js
class Foo {
  get Bar() {
    return Bar;
  }
}
class Bar {};

// File Blah.js
const Foo = require('./Foo.js');
class Blah extends Foo.Bar {}      <--- Error would be here

Is fixed by this...

/**
 * @type Class
 */
get Bar() {
  return Bar;
}
Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129
Arei
  • 91
  • 1
  • 1
3

The best way to fix this is to export the class with ES6 compatibility; MyClass is exported using CommonJS format when you write module.exports, but not in ES6 format. For MyClass you can do one of the following in mod1.js:

export class MyClass {
    constructor(name) {
        this.name = name;
    }
}

module.exports = {
    MyClass,
};

Or, my recommended way, as it's more visible:

class MyClass {
    constructor(name) {
        this.name = name;
    }
}

module.exports = {
    MyClass,
};

exports { MyClass };

Try one of these and see if you get results.

Arcsector
  • 1,153
  • 9
  • 14
  • I tried both your solution and work for the tsc compiler, but if I try to run the original JS code in node or to browserify it, it fails since the files are modules in commonJS format, and commonJS does not support "export class", nor exports { stuff}... there is another workaround? – gabry Oct 21 '20 at 05:51