TypeScript: Generating Type Definitions from Javascript is blocked by requiring private name

TypeScript Version: 3.9.0-dev.20200407

Search Terms:

  • TS9006
  • Declaration emit for this file requires using private name
  • Explicit type annotation
  • Unblock declaration emit.

Code

// thing.js // maybe something from deep in my library
'use strict';
class Thing {}
module.exports = { Thing };
// index.js
"use strict";
const Thing = require("./thing").Thing;
module.exports = { Thing }; // re-export to the world

tsconfig.json:

{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": true,
    "declaration": true,
    "emitDeclarationOnly": true,
    "declarationDir": "types",
    "strict": false,
    "target": "ES2018",
    "module": "commonJS",
    "moduleResolution": "node",
    "lib": ["ES2018"]
  },
  "include": ["index.js", "thing.js"]
}

Expected behavior: I believe I should get two declaration files out of this that would look something like:

// thing.d.ts
export class Thing {}
// index.d.ts
import { Thing } from './thing';
export { Thing };

Actual behavior: index.d.ts fails to be emitted with the error:

TS9006: Declaration emit for this file requires using private name 'Thing' from module '"/Users/neal/Desktop/privateNameTSC/thing"'. An explicit type annotation may unblock declaration emit.

It’s not clear to me how to annotate the module.exports to make the “name” not private anymore Thanks for taking a look.

Playground Link: Can’t because imports related but I made a minimum repro here: https://github.com/nbbeeken/private-name-error-tsc

Related Issues: #9865

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 27
  • Comments: 29 (6 by maintainers)

Commits related to this issue

Most upvoted comments

@nbbeeken Try to "allowJs": false, I think it helps you.

Are you sure this is fixed in 4.1?

npm install typescript@beta

I have the same problem both on 4.0.3 and 4.1, not sure is the exact same problem, minimal example included here:

https://stackoverflow.com/questions/64460213/create-typescript-declaration-files-for-existing-jsdoc-annotated-commonjs-librar

… maybe I did something wrong? Should I open a new ticket with the code I posted in stackoverflow?

Came to this issue because I was getting the same error, even when I had declaration: false. I just tested the repro (https://github.com/nbbeeken/private-name-error-tsc) with typescript@beta (4.1.0-beta) and it no longer produces this error.

Additionally, this bug still persist, see this repro: https://gist.github.com/Bnaya/f88130a9321b2706e1745f8695ff9664

When trying to avoid the above crash, i’ve set the imported class to an intermediate variable, and exported it. that leads again to the “using private name” error

This is fixed in latest master (likely by a combination of the other fixes going in) and now emits

//// [lib.d.ts]
/**
 * @param {string} a
 */
export function bar(a: string): string;
export class SomeClass {
    a(): number;
}
//// [main.d.ts]
export const IntermediateClass: typeof SomeClass;
import { SomeClass } from "./lib";

If anyone comes back to this thread using latest nightly TS and thinks they have “the same bug” - please open a new thread with your repro - tracking stuff in closed issues is hard - thanks~

@ggreco Will you create a new issue?

I created a new bug for this, https://github.com/microsoft/TypeScript/issues/41250

@Bnaya thanks for the workaround, it works, but it totally destroy the jsdoc markup for javascript users of the library…

Yep, this is fixed in 4.1 – although I found a related bug when requireing a relative path like "./thing" instead of "fs".

I am having the same error message without using explicit exports (tested with TypeScript 3.9.7). We have different implementations of classes with same name in different folders (for different customers). Although this results in a naming conflict, we would be able to @ts-ignore the duplicate declarations.

Sadly, some files produce an error that cannot be ignored: Error:(1, 1) TS9005: Declaration emit for this file requires using private name 'Bla'. An explicit type annotation may unblock declaration emit.

Minimal example:

// test1/Bla.js
class Bla extends BlaBase
{

}
// test2/Bla.js
class Bla extends BlaBase
{

}
// test3/BlaBase.js
class BlaBase
{
    constructor(name)
    {
        this.name = name;
    }
}

The error message is somewhat brittle. In this toy example it goes away when adding constructors to the two Bla implementations. However, in our real code I am unsure what is causing the issue (e.g. a specific method or field). Could you explain what is causing the message here? Any ideas to work around the issue?

@nbbeeken Try to "allowJs": false, I think it helps you.

I appreciate the help! unfortunately that doesn’t seem to work. The goal is to generate types from jsDoc comments so tsc needs to allow .js files as input. Here’s the errors you get if allowJs is false: (setting checkJs to false just removes the TS5052 error)

error TS5052: Option 'checkJs' cannot be specified without specifying option 'allowJs'.

error TS18003: No inputs were found in config file './types/tsconfig.json'. Specified 'include' paths were '["index.js","thing.js"]' and 'exclude' paths were '["types"]'.

Edit: Posting here as I don’t have a reduced reproducing example yet but wanted to share until I have one.

@nbbeeken, I had a case somewhat similar to yours, where our app was exporting a function that set a property (which I think TypeScript recognizes as a constructor):

module.exports = function(key) {
    const myImpl = implementations[key];
    this.invoke = myImpl.invoke;
};

and it was throwing this error: error TS9005: Declaration emit for this file requires using private name 'exports'. An explicit type annotation may unblock declaration emit.

By changing module.exports to exports, the compiler error went away. I’m not sure if this actually worked, but it did get rid of the error. 🤷‍♂️

I am seeing a similar error message in TypeScript 4.3.2 with the following pattern:

/* data_listener.js */
export function createDataListener() {
    return async function dataListener(someObj, x, y) {
        // `this` references the model instance
       this.blah = something;
    };
}

/* create.js */
import {createDataListener} from "../data_listener";
someObj.setDataListener(createDataListener().bind(model, someObj));

I tried removing the this reference as a hint from the quoted comment (maybe TypeScript thinks the returned function is being used as a constructor function when it is not) and I’m able to compile that code:

/* data_listener.js */
export function createDataListener() {
    return async function dataListener(model, someObj, x, y) {
       model.blah = something;
    };
}

/* create.js */
import {createDataListener} from "../data_listener";
someObj.setDataListener(createDataListener().bind(undefined, model, someObj));

Thanks for the suggestion @jrnail23! I gave that a go but now there are type errors about exports not existing:

index.js:3:23 - error TS2306: File '.../test-ts-bug/thing.js' is not a module.
3 const Thing = require("./thing").Thing;
                        ~~~~~~~~~
thing.js:4:1 - error TS2304: Cannot find name 'exports'.
4 exports = { Thing };
  ~~~~~~~
Found 2 errors.

Here’s a quick screenshot to show I’m using the example in the issue description.

Screenshot:

@nbbeeken, I had a case somewhat similar to yours, where our app was exporting a function that set a property (which I think TypeScript recognizes as a constructor):

module.exports = function(key) {
    const myImpl = implementations[key];
    this.invoke = myImpl.invoke;
};

and it was throwing this error: error TS9005: Declaration emit for this file requires using private name 'exports'. An explicit type annotation may unblock declaration emit.

By changing module.exports to exports, the compiler error went away. I’m not sure if this actually worked, but it did get rid of the error. 🤷‍♂️

@sandersn you have it exactly.

@sandersn thank you kindly for the quick triage. This is a bit of a roadblock for our migration to TypeScript, would it be helpful if we tried to fix this locally? Or is there already someone on your end that will begin work shortly?