Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generating Type Definitions from Javascript is blocked by requiring private name #37832

Closed
nbbeeken opened this issue Apr 7, 2020 · 29 comments
Closed
Assignees
Labels
Bug A bug in TypeScript

Comments

@nbbeeken
Copy link

nbbeeken commented Apr 7, 2020

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

@sandersn sandersn added the Bug A bug in TypeScript label Apr 7, 2020
@sandersn sandersn added this to the TypeScript 4.0 milestone Apr 7, 2020
@sandersn
Copy link
Member

sandersn commented Apr 7, 2020

This is broken in 3.8 and 3.9 at least.

@mbroadst
Copy link

mbroadst commented Apr 8, 2020

@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?

@sandersn
Copy link
Member

sandersn commented Apr 8, 2020

From talking to @weswigham about a similar issue (probably the same issue), it's an unfortunate limitation in the way that we bind module.exports = { Thing }; although it looks like it could be translated to export { Thing }, we actually have to treat it as

declare const _tmp: { Thing: Thing }
export = _tmp

It's probably possible to special-case this exact pattern and emit export { Thing }.
@weswigham if you can weigh in, you know more about this than I do.

@mbroadst I put this in 4.0 assuming that the real fix would be complex.

@weswigham
Copy link
Member

@sandersn you have it exactly.

chronark added a commit to chronark/swapchain that referenced this issue May 10, 2020
Unfortunately this is useless right now because typescript does not
generate the types correctly yet. We have to wait for typescript >= 4.0

See microsoft/TypeScript#37832
@jamesopti
Copy link

Is there a workaround for this to prevent the CLI from erroring?

I tried "checkJs": false, but to no avail

@Anton-Loginov
Copy link

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

@nbbeeken
Copy link
Author

nbbeeken commented Jul 9, 2020

@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"]'.

@achimmihca
Copy link

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?

@jrnail23
Copy link

jrnail23 commented Sep 17, 2020

@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. 🤷‍♂️

@nbbeeken
Copy link
Author

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:

@molisani
Copy link
Contributor

molisani commented Oct 6, 2020

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.

@sandersn
Copy link
Member

sandersn commented Oct 7, 2020

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

@sandersn sandersn closed this as completed Oct 7, 2020
sandersn added a commit that referenced this issue Oct 7, 2020
```js
const x = require('./foo').y
```

was incorrectly using the unmangled require path as the temp name in
emit:

```
import ./foo_1 = require('./foo')
import x = ./foo_1.y
```

It now uses the imported identifier:

```
import x_1 = require('./foo')
import x = x_1.y
```

Discovered while fixing #37832
sandersn added a commit that referenced this issue Oct 9, 2020
```js
const x = require('./foo').y
```

was incorrectly using the unmangled require path as the temp name in
emit:

```
import ./foo_1 = require('./foo')
import x = ./foo_1.y
```

It now uses the imported identifier:

```
import x_1 = require('./foo')
import x = x_1.y
```

Discovered while fixing #37832
@ggreco
Copy link

ggreco commented Oct 21, 2020

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?

@Bnaya
Copy link

Bnaya commented Oct 21, 2020

re-exporting classes now leads to tsc crash "Unhandled alias declaration kind in symbol serializer!"
I've filed a bug report:
#41182

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

@glen-84
Copy link

glen-84 commented Oct 25, 2020

@sandersn @weswigham – Can this issue be reopened?

I get the following when trying to generate definitions for stylelint (using TS 4.1.0-dev.20201025):

lib/assignDisabledRanges.js:1:1 - error TS9006: Declaration emit for this file requires using private name 'CommentExt' from module '"postcss/lib/comment"'. An explicit type annotation may unblock declaration emit.

1 'use strict';
  ~~~~~~~~~~~~

lib/descriptionlessDisables.js:1:1 - error TS9006: Declaration emit for this file requires using private name 'CommentExt' from module '"postcss/lib/comment"'. An explicit type annotation may unblock declaration emit.

1 'use strict';
  ~~~~~~~~~~~~

lib/needlessDisables.js:1:1 - error TS9006: Declaration emit for this file requires using private name 'CommentExt' from module '"postcss/lib/comment"'. An explicit type annotation may unblock declaration emit.

1 'use strict';
  ~~~~~~~~~~~~

lib/utils/parseCalcExpression/parser.js:383:13 - error TS9005: Declaration emit for this file requires using private name 'Parser'. An explicit type annotation may unblock declaration emit.

383             var parser = (function () {
                ~~~

@Bnaya
Copy link

Bnaya commented Oct 25, 2020

@glen-84 can you please share a minimal repro?

@glen-84
Copy link

glen-84 commented Oct 25, 2020

@Bnaya,

You can just clone the stylelint repo, update TS, and add the following to the tsconfig.json file:

        "declaration": true,
	"emitDeclarationOnly": true,
	"outDir": "types-new"

(and optionally remove "noEmit": true, which is not needed)

Then run npx tsc.

@Bnaya
Copy link

Bnaya commented Oct 25, 2020

@glen-84 While the error is the same, it's not the same issue,
see my workarounds there:
https://github.com/stylelint/stylelint/pull/5004/files
(types are now emitted)

What we are missing there to make it nicer is a way to export type from ts-jsdocs.

Also: seems like you are depending there on types from @types/stylelint package,
I think it might trip typescript in the endgame when you have real stylelint package with it's own type, but that tries to import types from stylelint in expectation that it will go to @types/stylelint

@glen-84
Copy link

glen-84 commented Oct 26, 2020

@Bnaya,

What we are missing there to make it nicer is a way to export type from ts-jsdocs.

So this is triggered by the use of @typedef to define a type, instead of importing the type each time? Should it work?

Do you know if there is an existing GitHub issue for this?

Also: seems like you are depending there on types from @types/stylelint package

Where do you see that?

(note: I am not a maintainer of stylelint)

@Bnaya
Copy link

Bnaya commented Oct 26, 2020

So this is triggered by the use of @typedef to define a type, instead of importing the type each time? Should it work?

I believe something like that

Do you know if there is an existing GitHub issue for this?

I think not

Where do you see that?

Example: import('stylelint').DisabledRange

@glen-84
Copy link

glen-84 commented Oct 26, 2020

I think not

Okay, I'll probably open one.

Example: import('stylelint').DisabledRange

This resolves to the local types in the types directory, not an @types package. These types would likely be removed once they can be generated automatically via TypeScript.

@Bnaya
Copy link

Bnaya commented Oct 26, 2020

This resolves to the local types in the types directory, not an @types package. These types would likely be removed once they can be generated automatically via TypeScript.

I'de say to relocate them, and import them as regular source files, and not like so (It will break if you publish)
But let's stop hijacking this issue, if there's somewhere else you are working on this, i'de love to help

@ggreco
Copy link

ggreco commented Oct 26, 2020

Here is a minimal example, less than 20 lines in total, it gives the error of this bug both on typescript 4.0 that on 4.1 beta:

https://github.com/ggreco/doc2dec

  • checkout the repo
  • npm install
  • npm run build

@glen-84
Copy link

glen-84 commented Oct 26, 2020

@ggreco Will you create a new issue?

@Bnaya
Copy link

Bnaya commented Oct 26, 2020

Here is a minimal example, less than 20 lines in total, it gives the error of this bug both on typescript 4.0 that on 4.1 beta:

https://github.com/ggreco/doc2dec

  • checkout the repo
  • npm install
  • npm run build

Ugly workaround:
use import('./rectangle').Rectangle

@ggreco
Copy link

ggreco commented Oct 26, 2020

@ggreco Will you create a new issue?

I created a new bug for this, #41250

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

@weswigham
Copy link
Member

weswigham commented Oct 28, 2020

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~

@tttinkl
Copy link

tttinkl commented Apr 22, 2021

I used require(./xxx.js) to skip this error; but i don't konw why it works.

nicoboss added a commit to nicoboss/practical-MPC that referenced this issue Oct 21, 2021
…hwierig dabei war der Dies war schwierig dabei war share.js da dort ein TyperScript Kompilierfehler ohne Zeilenangabe auftrat. Dieser war auf ein Bug in TypeScript zurückzuführen konnte durch eine @type JSDocs annotation behoben werden. Siehe microsoft/TypeScript#37832 welches obwohl es als geschlossen wurde immer noch auftritt.
@rajsite
Copy link

rajsite commented Oct 17, 2022

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));

Offroaders123 added a commit to Offroaders123/node-cdb that referenced this issue Feb 12, 2024
Not sure how to default export from JSDoc-powered JS, when in CommonJS. Once I move to ESM and or TS it's not an issue, but I was kind of surprised that this didn't work already, since it's an existing practice for Node packages that have been around for a while. I'm probably just doing it wrong, but I'm not totally sure. I thought it might've been the part where it was assigning both the variable and the `module.exports` in the same line, but that didn't fix the errors either.

microsoft/TypeScript#37832
https://stackoverflow.com/questions/60009092/declaration-will-not-emit-due-to-private-name-usage
microsoft/TypeScript#2719

Super bowl!!!!! aaaeaaeggh, 380 green hut, AA
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript
Projects
None yet
Development

No branches or pull requests