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

XXX has or is using name 'Foo' from external module "../bar" but cannot be named #9944

Closed
vilicvane opened this issue Jul 26, 2016 · 64 comments
Assignees
Labels
Domain: Declaration Emit The issue relates to the emission of d.ts files Fixed A PR has been merged for this issue Suggestion An idea for TypeScript

Comments

@vilicvane
Copy link
Contributor

vilicvane commented Jul 26, 2016

TypeScript Version: 2.0.0

npm init
npm install clime --save

tsconfig.json

{
    "compilerOptions": {
        "target": "es6",
        "module": "commonjs",
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "noEmitOnError": true,
        "noImplicitAny": true,
        "noImplicitReturns": true,
        "noImplicitThis": true,
        "strictNullChecks": true,
        "declaration": true,
        "sourceMap": true,
        "rootDir": "src",
        "outDir": "bld"
    },
    "exclude": [
        "node_modules"
    ]
}

src/cli.ts

#!/usr/bin/env node

import * as Path from 'path';
import { CLI, Shim } from 'clime';

let cli = new CLI('henge', Path.join(__dirname, 'commands'));

// This is okay.
export default cli;

// This gives errors:
// TS4023: Exported variable 'foo' has or is using name 'Promise' from external module "C:/Projects/clime/node_modules/thenfail/bld/promise" but cannot be named.
// TS4023: Exported variable 'foo' has or is using name 'Printable' from external module "C:/Projects/clime/bld/core/object" but cannot be named.
let foo = cli.execute([]);
export { foo };

It seems that if I import Printable from clime, the second error would go away, but I cannot import Promise from the dependency of clime.

Is this expected behavior or a bug?

@vilicvane vilicvane changed the title How to solve error: XXX has or is using name 'Foo' from external module "../bar" but cannot be named XXX has or is using name 'Foo' from external module "../bar" but cannot be named Jul 26, 2016
@mijay
Copy link

mijay commented Sep 2, 2016

This (or very similar) issue actually completely breaks declaration feature in projects using external typings:

test.ts

import { DOM, Component } from "react";
export class TestMe extends Component<{}, {}> {
// error TS4055: Return type of public method from exported class has or is using private name 'React.DOMElement'.
// error TS4055: Return type of public method from exported class has or is using private name 'React.HTMLAttributes'.
    render() {
        return DOM.div();
    }
}

tsconfig.json

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es5",
    "moduleResolution": "node",
    "declaration": true
  },
  "files": [ "test.ts" ]
}

package.json

{
  "name": "test",
  "version": "1.0.0",
  "dependencies": {
    "@types/react": "^0.14.31",
    "react": "^15.1.0"
  },
  "devDependencies": {
    "typescript": "^2.0.2"
  }
}

I can fix the errors by explicitly importing DOMElement and HTMLAttributes, but that negate all benefits of type inference - I am basically forced to write all types everywhere (especially with noUnusedLocals).

@mhegazy
Copy link
Contributor

mhegazy commented Sep 2, 2016

There is no explicit type annotation on the functions or variables. the declaration emitter infers their type and tries to write it. if the type is coming from a different module, then a. it needs to add an import or b. error.

The emitter can write the additional import, but that would have been changing your API shape in a way you did not indicate clearly in your code. so we opted to error instead.

the fix would be to add an explicit type annotation on the source of the problem.

Having siad that, i think we should reconsider this design decision, and add imports anyways.

@mhegazy mhegazy added Suggestion An idea for TypeScript In Discussion Not yet reached consensus labels Sep 2, 2016
@mhegazy mhegazy added this to the TypeScript 2.1 milestone Sep 2, 2016
@mhegazy mhegazy self-assigned this Sep 2, 2016
@frederickfogerty
Copy link

Please consider adding adding imports to resolve this error. This is an error which comes up daily in our workflow with TypeScript and would be easier for us if this was never an issue again.

🙌

@landonpoch
Copy link

I was able to get around this issue by doing what @mhegazy suggested. After adding an explicit type annotation (my return type was blank and having to get inferred from the parent class) then the errors went away. Worked like a charm. Thanks!

@ecraig12345
Copy link
Member

ecraig12345 commented Oct 19, 2016

My team at Microsoft is having this issue too, but in our case (a mapping of enum values to functions) it's not reasonable to add explicit types. Here's a greatly simplified version of our scenario:

tsconfig.json:

{
    "compilerOptions": {
        "module": "amd",
        "target": "es5",
        "declaration": true
    }
}

Mapping.ts:

import { fooFunction } from './FooFunction';
import { barFunction } from './BarFunction';

export enum Enum {
    foo,
    bar
}

export const mapping = {
    [Enum.foo]: fooFunction, // error TS4023: Exported variable 'mapping' has or is using name 'IFoo' from external module "D:/test/IFoo" but cannot be named.
    [Enum.bar]: barFunction // error TS4023: Exported variable 'mapping' has or is using name 'IBar' from external module "D:/test/IBar" but cannot be named.
}

FooFunction.ts:

export interface IFoo {
    bar: string;
}
export function fooFunction(foo: IFoo) {
    return foo.bar;
}

BarFunction.ts:

export interface IBar {
    foo: string;
}
export function barFunction(bar: IBar) {
    return bar.foo;
}

An manually written interface for the mapping would be incredibly ugly and hard to maintain (the actual mapping has 20+ values):

interface IMapping {
    "0": (foo: IFoo) => string;
    "1": (bar: IBar) => string;
}

In our case, I worked around the issue by adding the relevant imports to the mapping file and wrapping them in tslint:disable (we disallow unused variables with a lint rule rather than a compiler flag). But it would be better if that wasn't necessary.

@unional
Copy link
Contributor

unional commented Oct 19, 2016

As a reminder that auto re-export or other mechanisms are needed to properly resolve this issue. Adding import only is not sufficient.

#5711 (comment)

----------- Original comment ----------

This could have deeper consequences.

Consider moduleA -> moduleB -> moduleC -> moduleD.

moduleB is the one that needs to do import { ABC } from 'moduleA' to get around this issue.

When moduleC uses moduleB and export its signature, it again fails to compile because moduleB needs ABC from moduleA but this time moduleB didn't export it.

This means either:

  1. moduleC needs to have a hard dependency of moduleA and import ABC
  2. moduleB needs to not just import but also re-export ABC and then moduleC imports it.

If moduleD does similar things, then basically you need to know the whole chain of dependencies.

I wasn't able to test this to prove that it is the case because I'm using typings and there is an issue blocking me so: typings/typings#625

EDIT: I am able to reproduce it and indeed my moduleD needs to reference moduleA to import ABC.
In my example:

  • moduleA => redux
  • moduleB => redux-thunk
  • moduleC => custom code on top of redux-thunk
  • moduleD => some library
  • ABC => Dispatch interface in redux

@mhegazy
Copy link
Contributor

mhegazy commented Oct 19, 2016

An manually written interface for the mapping would be incredibly ugly and hard to maintain (the actual mapping has 20+ values):

you do not need an interface, you just need to import the types:

Mapping.ts:
import { fooFunction, IFoo } from './FooFunction';
import { barFunction, IBar } from './BarFunction';

...

Edit: fixed the sample above, thanks for @vladima for point out.

@mischkl
Copy link

mischkl commented Dec 7, 2016

Just came across this issue today after upgrading Angular-CLI.

Needless to say, from my perspective as a "naive" developer, the requirement that implicitly used interfaces be imported everywhere has the serious downside of poor developer tooling support. At least in WebStorm using the TypeScript service, the imported interfaces are shown as being unused and thus subject to removal by other developers or automatic removal whenever Optimize Imports is executed. Aside from that it is extremely non-obvious, from the wording of the error message, how the problem can be fixed.

@ghost
Copy link

ghost commented Mar 6, 2017

Ran into this too. Here's a repro.

// @declaration: true
// @noUnusedLocals: true

// @Filename: a.ts
export class A {}
export var a: A;

// @Filename: b.ts
import { a, A } from "./a";
export const b = a;

// @Filename: c.ts
import { a } from "./a";
export const c = a;

This gets errors in both b.ts and c.ts.

@borislemke
Copy link

I ran into this issue when started to destructuring my express app into smaller modules.
Could someone kindly help me out how to cope with the issue in this example?

import * as express from 'express'
...

export const WebsitePagesRouter = express.Router();

Error:
Exported variable "WebsitePagesRouter" has or is using name 'Router' from
external module '~path/to/@types/express-serve-static-core/index' but cannot be named.

@ghost
Copy link

ghost commented Mar 23, 2017

@borislemke This error only happens when the type of an export is implicit, so try adding an explicit type declaration, as in export const WebsitePagesRouter: XXX = express.Router().

@borislemke
Copy link

@andy-ms ahh now I got it. Thanks for the quick reply.
Simply adding the type fixed it

import {Router} from 'express'
export const MediaBundleRouter: Router = express.Router();

@gund
Copy link

gund commented Mar 28, 2017

@andy-ms you have a valid point but the real power of TS is that it is smart enough to resolve types implicitly based on return type of the function for example. And if you have a large codebase built on top of another one it is pointless to repeat types if they can be resolved implicitly.

I would suggest that it might be a good idea to make a feature like auto re-import in declaration files as an opt-in option in TS config file, something like "implicitDeclaration": true.

In this case default behavior will remain the same unless user knows what he wants and how it might modify his/her declaration files.

@trusktr
Copy link
Contributor

trusktr commented Dec 3, 2018

@weswigham I'm on typescript v3.3.0-dev.20181129, and it looks like this issue is not completely fixed.

The following issue has a sample of what my code looks like along with the problem described here: #28754 (comment)

Unfortunately I am not able to make a small reproduction, and I can't share the larger private code where it is happening.

The strange thing is that it only happens in some files, but not all of them.

vladimiry added a commit to vladimiry/fs-no-eperm-anymore that referenced this issue Mar 6, 2019
monkindey referenced this issue in monkindey/uform Jun 5, 2019
@hutber
Copy link

hutber commented Feb 10, 2020

For me, the error was simply that I had no declared an import at all.

import styled from 'styled-components'
styled was completely undefined, but instead of a normal JS error I got a TS error.

evanpurkhiser added a commit to evanpurkhiser/prolink-connect that referenced this issue May 14, 2020
lucamilanesio pushed a commit to GerritCodeReview/gerrit that referenced this issue Oct 21, 2020
The following 2 issues appears internally:
microsoft/TypeScript#15870
microsoft/TypeScript#9944

Change-Id: I60b669846447ff7f54e7b0f741402333c2b59653
lucamilanesio pushed a commit to GerritCodeReview/gerrit that referenced this issue Nov 11, 2020
The following 2 issues appears internally:
microsoft/TypeScript#15870
microsoft/TypeScript#9944

Change-Id: I60b669846447ff7f54e7b0f741402333c2b59653
@8bu
Copy link

8bu commented Feb 10, 2021

Don't wanna bring this issue up again but I'm really struggling with this error. I read through the comments but can't really understand my problem & solution provided above.

The error

Exported variable 'useWeb3' has or is using name 'RedBN' from external module "~/sample-project/node_modules/@types/bn.js/index" but cannot be named.

My code:

// example.ts

import Web3 from 'web3';
import { ref, toRefs } from 'vue';

const web3 = ref<null|Web3>(null);
export const useWeb3 = () => {
  return {
    state: toRefs(web3)
  }
}

From what I've read in this issue, I just need to import the RedBN type to my example.ts ? But RedBN was declare locally & have no export then how can I resolve this problem..

@jcbdev
Copy link

jcbdev commented Dec 12, 2021

@8bu, @kohlmannj, @trusktr

I had this for an exported type which is actually just a renamed internal interface which could not be imported/re-exported so I found a work around:

// TSESTreeOptions is exported from library but its actually just a rename of an internal type ParseAndGenerateServicesOptions which is not exported and this causes the error in type resolution
import { TSESTreeOptions as _TSESTreeOptions } from '@typescript-eslint/typescript-estree';

//Clone type and use this type in place of the original for my modules export api/signature
export type TSESTreeOptions = { [K in keyof _TSESTreeOptions]: _TSESTreeOptions[K] }; 

export function doSomething(options: TSESTreeOptions) {
    callFunctionThatRequiresOriginalType(options); //Type resolution should know types are compatible
    callFunctionThatRequiresOriginalType(options as _TSESTreeOptions); //If not, cast to original type
}

This is the original type export from the @typescript-eslint/typescript-estree library.

export declare type TSESTreeOptions = ParseAndGenerateServicesOptions;

but if you find ParseAndGenerateServicesOptions its an internal interface

interface ParseAndGenerateServicesOptions extends ParseOptions {

My theory is this type of export is the issue as typescript sees that TSESTreeOptions = ParseAndGenerateServicesOptions and potentially just treats it as an alias, or it doesn't work when "exported type = internal interface"? Then when it tries to resolve this alias if you import and then export from another module further down the line the alias cannot be resolved because the type is resolves is internal?

@btakita
Copy link

btakita commented Nov 13, 2022

This issue makes opaque types very difficult to use in some situations.

declare const norm1_5_sym:unique symbol
export type norm1_5_T<Base extends number = number> =
	Base extends score_T
	? score1_5_T
	: Base extends percentile_T
		? percentile1_5_T
		: Base&{ [norm1_5_sym]:any }
// TS4023: Exported variable 'norm1_5_M_color' has or is using name 'norm1_5_sym' from external module "..." but cannot be named.
export const norm1_5_M_color = Object.freeze(
	new Map<norm1_5_T, string>([
		[null as norm1_5_T, norm1_5__null__color],
		[1 as norm1_5_T, norm1_5__1__color],
		[2 as norm1_5_T, norm1_5__2__color],
		[3 as norm1_5_T, norm1_5__3__color],
		[4 as norm1_5_T, norm1_5__4__color],
		[5 as norm1_5_T, norm1_5__5__color],
	]))

Even adding the following does not fix the TS4023 error:

import type { norm1_5_sym } from '@namespace/types'
type _ = typeof norm1_5_sym

I have not been able to export/import norm1_5_sym to make the error go away. norm1_5_sym is meant to be private to apply the opaque type hack to add typing info to a primitive.


Edit: I was able to fix the issue by extracting a base type from the norm1_5_T definition:

declare const norm1_5_sym:unique symbol
type base__norm1_5_T<Base extends number = number> = Base&{ [norm1_5_sym]:any }
export type norm1_5_T<Base extends number = number> =
	Base extends score_T
	? score1_5_T
	: Base extends percentile_T
		? percentile1_5_T
		: base__norm1_5_T<Base>

Perhaps there's an issue with defining the base norm1_5_T within the conditional logic without there being a type to reference.

Note that score1_5_T & percentile1_5_T are defined as:

declare const percentile_sym:unique symbol
export type percentile_T = number&{ [percentile_sym]:any }
declare const percentile1_5_sym:unique symbol
export type percentile1_5_T = norm1_5_T&percentile_T&{ [percentile1_5_sym]:any }
declare const score_sym:unique symbol
export type score_T = number&{ [score_sym]:any }
declare const score1_5_sym:unique symbol
export type score1_5_T = norm1_5_T&score_T&{ [score1_5_sym]:any }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Domain: Declaration Emit The issue relates to the emission of d.ts files Fixed A PR has been merged for this issue Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests