Skip to content

Difficulties with --declaration flag (error TS4025: Exported variable has or is using private name) #23110

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

Closed
evil-shrike opened this issue Apr 3, 2018 · 8 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@evil-shrike
Copy link

evil-shrike commented Apr 3, 2018

I'm trying to compile my lib with --declaration flag. As it's the only officially approved way to distribute libraries. But I'm having lots of errors from the compiler.

In general all errors are related to different visibility of elements. But error codes are different depending on context. But mostly errors are (examples):
error TS4025: Exported variable 'html' has or is using private name 'htmlBind'.
error TS4031: Public property '_currentArea' of exported class has or is using private name 'AreaInternal'.
error TS4055: Return type of public method from exported class has or is using private name 'PartHelper'.
error TS4073: Parameter 'partHelper' of public method from exported class has or is using private name 'PartHelper'.
error TS4078: Parameter 'options' of exported function has or is using private name 'ExtendOptions'.

Mostly of them makes sense, but not all.

Here I'd like to discuss TS4025.

I have a module with a local function which isn't exported. I want to export it under another name.

export interface IBindable {}
function htmlBind (el: JQuery|HTMLElement, options?: string | html.Options): IBindable {}

export const html = htmlBind;
export namespace html {
	export interface Options {
	}
}

but here TSC produces the error:
binding.ts(527,14): error TS4025: Exported variable 'html' has or is using private name 'htmlBind'.

Of cause I could name the function as html and export:

export function html  {}

But inside the module I'd like to use other (more specific) name.
What I actually need to do is to export function as a separate statement, like (doesn't work)

export htmlBind as html;

Also in other cases I have similar problem for namespaces (instead of function). I want to declare a namespace and then export it.
Given a module with export=

namespace indexedDBUtils {
    export const isSupported: boolean = !!window.indexedDB;
}
class DataStoreIndexedDB {
	utils: typeof indexedDBUtils;
}
DataStoreIndexedDB.mixin({
	utils: indexedDBUtils
});
export = DataStoreIndexedDB;

This fails to compile with error TS4031: Public property 'utils' of exported class has or is using private name 'indexedDBUtils'.
So I have to export the namespace indexedDBUtils.

class DataStoreIndexedDB  {
	utils: typeof DataStoreIndexedDB.indexedDBUtils;
}
namespace DataStoreIndexedDB  {
    export namespace indexedDBUtils {
        export const isSupported: boolean = !!window.indexedDB;
    }
}
DataStoreIndexedDB.mixin({
	utils: DataStoreIndexedDB.indexedDBUtils
});
export = DataStoreIndexedDB ;

The problem is that now I have to use very long identifiers like DataStoreIndexedDB.indexedDBUtils.isSupported.
It'd be nice to declare namespace as before and then export it (doesn't work):

namespace indexedDBUtils {
    export const isSupported: boolean = !!window.indexedDB;
}
class DataStoreIndexedDB {
	utils: typeof indexedDBUtils;
}
namespace DataStoreIndexedDB  {
    export indexedDBUtils;
}
DataStoreIndexedDB.mixin({
	utils: indexedDBUtils
});
@mhegazy
Copy link
Contributor

mhegazy commented Apr 3, 2018

For background, the declaration emitter will not add an import to a module that was not originally imported; nor would it make a declaration that was previously un-exported exported. that is why it requires the user to import a module or export a declaration if it is needed to write the type of a declaration in the current file. For more information see #9944.

The expected fix when you see errors about unexported declaration is one of two things, either 1. export the declaration in question, or 2. write an explicit type annotation for the declaration that is exported and is using the unexported declaration.

in the example above, your declaration of htmlBind should look something like:

export interface IBindable {}
function htmlBind (el: JQuery|HTMLElement, options?: string | html.Options): IBindable {}

export const html: (el: JQuery|HTMLElement, options?: string | html.Options) => IBindable = htmlBind;
export namespace html {
	export interface Options {
	}
}

@mhegazy mhegazy added the Question An issue which isn't directly actionable in code label Apr 3, 2018
@evil-shrike
Copy link
Author

Ok, it's clear, thanks.
But w/o going into details I should say that first impression is that it's a bit redundant declaration:

function htmlBind (el: JQuery|HTMLElement, options?: string | html.Options): IBindable {}
export const html: (el: JQuery|HTMLElement, options?: string | html.Options) => IBindable = htmlBind;

The expected fix when you see errors about unexported declaration is one of two things, either 1. export the declaration in question, or 2. write an explicit type annotation for the declaration that is exported and is using the unexported declaration.

this makes sense.
but most difficulties arise in "export=" modules.
it's turning out that in "export=" modules we can'y use any interfaces or types in exported signatures at all and should put them all inside merged namespace.

just another example:

class TreeNode {}
class Tree {
	Node: typeof TreeNode;
}
Tree.prototype.Node = TreeNode;
namespace Tree {
	export import Node = TreeNode;
}
export = Tree;

that TreeNode type is broadly used in all methods of Tree. Now I have to put it inside Tree namespace:

class Tree {
	Node: typeof Tree.TreeNode;
}
Tree.prototype.Node = Tree.TreeNode;
namespace Tree {
	export class TreeNode {}
}

I seems very naturally to declare a type alias on root and continue to use short name (at least for type):

type TreeNode = Tree.TreeNode;

but it won't work. It's already was suggested in #14286. So my +1. It'd help a lot.

@mhegazy
Copy link
Contributor

mhegazy commented Apr 3, 2018

But w/o going into details I should say that first impression is that it's a bit redundant declaration:

it is. the intention is to warn you about exposing internal implementation details. it is possible you want to expose this as {} for instance to avoid users depending on its behavior.

@mhegazy
Copy link
Contributor

mhegazy commented Apr 3, 2018

The underlying issue here is that originally we took a more strict interpretation of visibility.. the definition really meant that the user of the API can refer to the type name. in this case Tree.Node has a type (TreeNode) that your API users have no way of referring to..
I think in retrospect that is not a practical definition. i think the only thing the emitter should care about is exposing something that was not already exposed, whether users can refer to them or not, is not that big of an issue.

@mhegazy
Copy link
Contributor

mhegazy commented Apr 3, 2018

Filed #23127 to track that.

@evil-shrike
Copy link
Author

Thanks, let me clarify a bit.
for the given ts source:

type TreeNode = Tree.TreeNode;
class Tree {
	Node: typeof TreeNode;
	function addNode(node: TreeNode) {}
}
namespace Tree {
	export class TreeNode {
		title: string;
		data: any;
	}
}
export = Tree;

will we get a declaration where Tree.addNode's node argument will have a type Tree.TreeNode?
I'm just curious about "relaxing" statement, it can also mean replacing types with any or something else.

@mhegazy
Copy link
Contributor

mhegazy commented Apr 4, 2018

will we get a declaration where Tree.addNode's node argument will have a type Tree.TreeNode?

yes. your .d.ts file will have the type. but your users will not be able to use the type name TreeNode. since this is a structural type system they can create an argument that is assignable to TreeNode

@izengliang
Copy link

const pkey = Symbol.for("pkey");  //  have compile error!
// Exported variable has or is using private name

export const pkey = Symbol.for("pkey");   // is work!

I think Symbol.for(...) should not be wrong!

@microsoft microsoft locked and limited conversation to collaborators Jul 31, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

3 participants