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

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?

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 150
  • Comments: 64 (16 by maintainers)

Commits related to this issue

Most upvoted comments

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.

šŸ™Œ

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.

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.

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

@mdekrey an even harder case with this and noUsusedLocals:

// create.ts
export interface Foo { }

export function create(foo: Foo) { return foo }

// x.ts
// import { create } from './create' // missing private type Foo
import { create, Foo } from './create' // unused local Foo

export const abc = { create } // no place to use Foo !!

This is unfortunately a much larger issue with the new noUnusedLocals compiler switch; we can no longer just import the typing from the external module due to causing the new error XXX is declared but never used. Instead, we have to make sure the imported value is actually used somewhere.

@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().

As a relative newcomer to TypeScript, it’s dismaying to see this two-year-old issue still in the discussion phase, especially since it amounts to mixed messaging from the TypeScript compiler, which in turn adds unnecessary confusion and ambiguity.

A slice of my experience: TSLint’s no-unused-variable rule is reliant on the TypeScript compiler’s noUnusedLocals option (example scenario with React), but enabling said option generates false positives for imports of external type definitions.

This feels like a kind of design stalemate, which leaves me without any clear sense of which code quality tool* or configuration settings might allow me to reliably assess unused imports and local variables in my own TypeScript source files while also maintaining comprehensive type definitions.

Going all the way back to @gund’s comment and @disintegrator’s comment, it seems like there are at least two viable options:

  • Option 1 (my preference): using "declaration": true and "noUnusedLocals": true prevents false positives for external type definition imports required to name any in-scope types
  • Option 2: a third flag (e.g. @gund’s "implicitDeclaration": true) be added to explicitly cover this scenario

[Edit] I suppose this is a relatively advanced use / edge case which primarily affects library authors. More than happy to keep things in perspective. All things considered, I would love to see this particular discussion continue — working with TypeScript has been very good overall, and this is really only my first major ā€œgotchaā€. šŸ’ÆāœØ

—

* At some point I had ESLint + TypeScript working, but I’m not convinced that ESLint + Babel 7 + TypeScript + eslint-typescript-parser is a reliable and/or great developer experience as of this writing (May 2018).

Im new to typescript, but when a compiler complains to me that it cant find an import, because one dependency explicitly import its, but another imports it under an alias, thats just a bug, not ambiguity, or a feature, or being explicit… its a bug. Either dont let me do it at anywhere - or it needs to be supported.



import * as C1 from "./common"
import { C } from "./common" //required to suppress error
import { B } from "/b"
class A {
    run() {
        b.run();
        C1.Do();
    }
}


import { C } from "./common"
class B {
    run() {
        C.Do();
    }
}

What’s the status of this? I can’t even begin to list the number of times I’ve had to help people with this issue. At least several times a week in our projects. There’s something urgently wrong here.

We just changed the return type of graphql-tag from any to DocumentNode from the graphql package, which because of this issue is a breaking change and needs to be postponed to the next major https://github.com/apollographql/graphql-tag/issues/150 It would be nice if operations like that were safe

Any movement on this now the new emitter has been merged? This would allow us to cleanup so many useless imports/re-exports.

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.

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…

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

https://github.com/Microsoft/TypeScript/issues/5711#issuecomment-231501080

----------- 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: https://github.com/typings/typings/issues/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

I’m happy to import the types from external modules explicitly. I just wish there was a way to have --noUnusedLocals understand this scenario instead of flagging as error. This issue is quite important to library authors that want to use TypeScript to write library code[1] that depends on third party modules.

[1]: meaning code that is distributed with declarations

the concern is just you depend on a library X, it might be an implementation detail, that you may want to change without changing your API. the rational is, you can explicitly add the type annotation if you are fine having X as part of our API, or change it to something else.

You mean when a JS client would be using undocumented internals, a TS client would get a fully typed public api for an internal dependency? I can see where someone might want that, but that would be something to solve when you have the ability for TS to know what an internal API even is! For example, if type declaration merging becomes a thing, then it makes sense that the merged file does not include declarations you didn’t ask for. At the moment, without that, and the weird way package.json types field works, I don’t think there’s a way to have an NPM package with external submodules, which is a reasonable pattern - tree-shaking isn’t permitted to remove module-init code, and packages can have different ā€œmodesā€ of use (react-devtools-extension/logOnlyInProduction, core-js/lib/*, rxjs/operators)

So I would ask for the current 1:1 .ts to .d.ts model for --declarations to be ā€œjust make it workā€ in the absence of future options, given how awkward and arduous it can be to work with the current behavior.

Another option that keeps the current behavior without requiring the complexity of declaration merging may be: --publicFiles, which requires manual annotations only the exports of the listed files? And hopefully editors/doctools respect it and only expose those files (e.g. in path completion).

@weswigham do you believe the new declaration emitter will fix this, or should we consider this as a standalone bug?

The workaround for this at the moment seems to be to import the used types. However, unless you use this elsewhere in your code file (and have --noUnusedLocals enabled) you’ll then be warned that you’re importing something but aren’t using it.

Because of these two conflicting issues I have to awkwardly include some reference to the type in my files. For example, all of my styled-components code files that export their components have this abomination somewhere in the file just to keep TypeScript happy:

((_?: StyledComponentClass<any, any>) => 0)();

I don’t know if this is a genuine fix or just masking an underlying issue I don’t understand.

The alternative being to have something like:

const Header: StyledComponentClass<React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>, any, React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>>
 = styled.h1`

Which is even worse.

To every commenting that they have the same issue, please šŸ‘ the top comment. This allows the maintainers to easily see the magnitude of this issue.

I know other Microsoft teams like VS Code use šŸ‘ s to rank features/issues to be implemented/fixed.

+1 for this problem.

I do not think add an explicit type annotation is a good way, since it’s hard to maintain.

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.

@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: https://github.com/Microsoft/TypeScript/issues/28754#issuecomment-443820268

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.

Since this issue has been closed, here’s some follow-up from my previous comment: https://github.com/Microsoft/TypeScript/issues/9944#issuecomment-385599123

One of my concerns, which led me to this issue, was that TypeScript’s ā€œcannot be namedā€ error was telling me to import an additional type. When I did that, I got a warning about the imported type not being used, since I have "noUnusedLocals": true set in my tsconfig.json.

The solution was to explicitly declare my types. In VSCode, this is really easy: you can hover over a symbol, select the text in the tooltip, which shows that symbol’s automatically inferred type, and then paste that type declaration into your source file.

Et voilĆ  — at this point, you’ve referenced the imported type locally, The now-explicit type can be named by the TypeScript compiler, and the ā€œunused localā€ warning disappears. 🌈

Still doesn’t work for me: Typescript: 2.9.1-insiders.20180525 / 3.0.0-dev.20180522

Repro: file1.ts

import Color from "color";
export declare function styled(): Color;

file2.ts

import { styled }  from "./file1";
export const A = styled();

Exported variable 'A' has or is using name 'Color' from external module "/Users/asvetl/work/test/node_modules/@types/color/index" but cannot be named.

This is becoming a major annoyance for me at the moment.

I’ve created an internal library for wrapping my functions for various purposes. This is basic example of something similar:

Management.ts

export interface IOutput {
  status: number;
  errorMessage?: string;
}

export type TFunction<I = {}, O extends IOutput = any> = (options: I) => Promise<O>;

export interface IFunctionDescription {
  title: string;
  description?: string;
  id?: string;
}

export function createFunction<I, O extends IOutput>(description: IFunctionDescription, func: TFunction<I, O>): TFunction<I, O> {
  return async (functionInput: I): Promise<O> => {
    console.log(`Running function ${description.title}`);

    const response = await func(functionInput);

    if (response.status === 200) {
      return response;
    }

    throw new Error(`Something went wrong: ${response.errorMessage}`);
  }
}

MyColorFunction.ts

import { createFunction, IOutput, TFunction } from "./Management";

export interface IOMyFunctionInput {
  color: string;
}

export interface IOMyFunctionOutput extends IOutput {
  isFavourite?: boolean;
}

const myColorFunction: TFunction<IOMyFunctionInput, IOMyFunctionOutput> = async ({ color }) => {
  if (color !== "blue") {
    return {
      status: 401,
      errorMessage: "Not a real color!",
    }
  }

  return {
    status: 200,
    isFavourite: true,
  }
}

export const myColorFunctionCreated = createFunction<IOMyFunctionInput, IOMyFunctionOutput>({
  title: "My Function For Favourite Colours",
}, myColorFunction)

Finally at the end I want to keep these functions neatly namespaced inside a kind of ā€œServiceā€ object:

Usage.ts

import { myColorFunctionCreated } from "./MyColorFunction";

export const FunctionService = {
  myColorFunction: myColorFunctionCreated,
}

And the resulting error:

Error:(3, 14) TS4023: Exported variable 'FunctionService' has or is using name 'IOMyFunctionInput' from external module "D:/.../MyColorFunction" but cannot be named.
Error:(3, 14) TS4023: Exported variable 'FunctionService' has or is using name 'IOMyFunctionOutput' from external module "D:/.../MyColorFunction" but cannot be named.

Now, I would expect Typescript to be able to infer those types because createFunction<I, O> is clear about what types it is using when exporting myColorFunctionCreated.

It doesn’t even work if I be even more explicit on that export like this:

export const myColorFunctionCreated: TFunction<IOMyFunctionInput, IOMyFunctionOutput> = createFunction<IOMyFunctionInput, IOMyFunctionOutput>({
  title: "My Function For Favourite Colours",
}, myColorFunction)

The only way to mitigate this (and it gets very tedious very quickly - not to mention makes my code very unreadable after a while) is this:

Usage.ts

import { IOMyFunctionInput, IOMyFunctionOutput, myColorFunctionCreated } from "./MyColorFunction";
import { TFunction } from "./Management";

export const FunctionService = {
  myColorFunction: myColorFunctionCreated as TFunction<IOMyFunctionInput, IOMyFunctionOutput>,
}

Or, of course, to just have those imports in the file and not explicitly make use of them. But that causes other issues like others have mentioned (unused imports) and in most IDEs will complain or refactor out automatically because they are not being used.

Basically, we are required to redefine types at the latest stage of usage - even though these types have a very clear pathway to exactly what they are. This is really dirtying up the code base with unnecessary declarations all over the place.

I started looking into this, but I’m afraid that my knowledge of the TypeScript codebase will hinder me from making a PR for this all on my own: it’d be my first PR to TypeScript, and this is still flagged as a ā€œSuggestionā€ by @mhegazy anyway.

Reason for the change:

  • Declaration emitting causes additional unclear errors for developers that can be automatically avoided.
  • Declaration emitting with noUnusedLocals set to true can result in edge cases that are difficult to solve, especially while keeping the implicit nature of TypeScript clean.

My understanding of the proposal:

  • A new flag, shouldExportImplicitTypes, should be added to compilerOptions to allow developers to opt-in to this case. (I personally would propose that this eventually is made the default for "declaration": true.)
  • New import statements should be emitted in the declaration file only to accomodate the implicit dependencies due to the types.
  • No change should occur on the emitted JS files.

Changes that I believe need to happen:

  • isSymbolAccessible in checker.ts (approx line 2286 of the linked version) will need to be updated to provide a new SymbolAccessibilityResult result that specifies the new import statement, possibly using aliasesToMakeVisible.
  • A new additionalTypeImports map similar to usedTypeDirectiveReferences would be added to declarationEmitter.ts to allow injection of imports at the top of the file. Naming collisions would need to be observed and avoided, however these types would not be referenced anywhere outside of this file, since they weren’t imported before and wouldn’t be exported, so the naming could be something clearly auto-generated. Paths to the imported file would need to be relative and follow the module pathing conventions.
  • Compiler flag would need to be added somehow; I didn’t get that far.

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

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.

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!

Thanks @weswigham , that’s a good idea. I also found another workaround, using --stripInternal experimental flag.

This is my attempt to summarize of workarounds:

  • Import the missing types. This probably yields unused errors, so you may add an export:
import { type } from "./other-module";
export { type }
  • If your module is internal (i.e. it’s not exported directly or indirectly from the package entry point), there are 2 additional alternatives: a. Emit declarations in a separate step including only public files (previous comment). b. Use experimental compiler flag --stripInternal and add comments to your exports:
    /** @internal TS issue 9944 */
    export class Project {
        ...
    

@pmoleri yes, there is - disable declarations on your main build, then add a second build with declarationsOnly set, and only include your entry point file, rather than all of your TS in that build.

I’m happy to import the types from external modules explicitly. I just wish there was a way to have --noUnusedLocals understand this scenario instead of flagging as error.

It would be great if --noUnusedLocals would ignore names prefixed with an underscore, just like --noUnusedParameters. Then you could import type-only dependencies like so:

import { SomeType as _S } from 'other-package'

(This is currently possible with tslint’s equivalent no-unused-variable rule.)

I’m not sure I follow, @unional. The top of the output .d.ts will have the reference to the original export; TypeScript is tracking the location of the symbols by their original path already.

From your original example:

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.

I believe your option 1 is the better solution, as it does not require re-exporting:

moduleC needs to have a hard dependency of moduleA and import ABC

Re-exporting changes the contract of the module, including the JS in the case of types that also have emitted JS (such as classes). Adding an explicit dependency where there was previously an implicit dependency is a less breaking change (I could see an argument where it’s a non-breaking change, but that’s I believe the proposal should be opt-in), and only affects the typing… which is the source of the problem.

This is a particularly painful requirement limitation for lib code, and given the fact that ES2015 modules are statically defined, it’s sad to see that a game plan (at least) for ameliorating this issue has yet to be defined.