You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
TS4023: Exported variable 'X' has or is using name 'Y' from external module 'a/file/path' but cannot be named #5711
I'm attempting to create a set of external TypeScript modules as follows:
// ClassA.ts
export default class ClassA {
public method() { return true; }
}
// ModuleB.ts
import ClassA from './ClassA';
var moduleB = {
ClassA: ClassA
};
export default moduleB;
// ModuleA.ts
import moduleB from './ModuleB';
var moduleA = {
moduleB: moduleB
}
export default moduleA;
The idea is that this will be compiled down and supplied as a library. The plan is that the consumer of the library will instantiate an instance of ClassA as follows:
import moduleA from './ModuleA';
var anInstance = moduleA.moduleB.ClassA();
This looks as though it is compiling down to the expected JS, but I'm getting an error compiler error which I'm struggling to find further information about.
Using tsc v1.6.2
.../src/ModuleA.ts(3,5): error TS4023: Exported variable 'moduleA' has or is using name 'ClassA' from external module ".../src/ClassA" but cannot be named.
Can anyone here shed any light on this issue? Does what I'm trying to do make sense?
FWIW you'll usually get faster answers on Stack Overflow, but we do field well-phrased questions here.
The problem here is that you're using the --declaration flag, but have not provided a way for the compiler to do its job.
When trying to emit ModuleA.d.ts, the compiler needs to write an object type literal (e.g. { moduleB: { classA: *mumble?* } }) representing the shape of the module. But there isn't a name in scope that refers directly to classA, so it the type "cannot be named" and there's an error.
If you add an import of ClassA to ModuleA.ts, the error should go away.
Reacted by Костя Третяк, Harun Urhan, Mark Pieszak, Remo H. Jansen, Vijayaragavendiran, Kamran Ayub, pvasek, Alex0007, Stefan Rein, Sérgio Carneiro and 50 more
Reacted by Andrew
Reacted by David Salazar, Denis Knaack, Andrew Faulkner, _al.pt_old, Pelle Jacobs, Wayne State University - Capstone Course, Carlos Galarza, ichyr, Adel Bachene, Ovidiu Barabula and 2 more
Reacted by Andrew Faulkner, juno suárez, Dan W, _al.pt_old, Burak Tasci, robmacecs, Thibault Chevrin, Birgit Pohl, Wayne State University - Capstone Course, Carlos Galarza and 4 more
Hi Ryan - thanks for the advice RE stack overflow and the advice - adding the import to ModuleA got things compiling as expected.
A little context here - my goal here is indeed to generate declarations for my external modules. (Something I believe isn't quite working yet? #5039?) This import may be completely necessary but It feels a little strange for ModuleA to need to import the symbols that moduleB already exports. The result is that every time I add to ModuleB's object export, I'll need to add the import to ModuleA - either directly:
import {moduleB} from './moduleB';
import {ClassA} from './ClassA';
Or via ModuleB (in an effort to reduce specifying paths for ClassA in both places):
import {moduleB, ClassA} from './moduleB';
I know very little about the TS compiler - so maybe what I'm saying is completely unrealistic, but at first thought it feels like the compiler could deduce that ModuleB is exporting an object with N symbols on it, so ModuleA needs those symbols? The import information for ModuleB is already there - could ModuleA use that? Or would that be impossible because the import for ClassA is completely internal to ModuleB, so there's no way that the compiler can deduce the type for ClassA when creating definitions for ModuleA?
Thank you again for taking the time to answer. I've sorted my problem now so the above is mainly curiosity - there's no rush to answer.
The compiler will not add dependencies (i.e. import statements) that did not exist in the user code when it emits declarations. the error you are getting means that the compiler is trying to write a type annotation for an exported declaration but could not. this can have one of two reasons, either the name is not accessible, i.e. not imported in the current module, or there is a declaration that is shadowing the original declaration.
in both cases, your work around is to add explicit type annotation, if you add any type annotation, it will be emitted verbatim in the output; the other option is to make sure the name is accessible, i.e. you have an import to the module, and you understand what that means for your users that are importing your module.
Reacted by Vladimir Iliev, Remo H. Jansen, Pascal Blessing, Paulo Cesar, Andrea Richiardi, Mark Penner, Charles Chang, Grigore Danciu, Ryan Taylor, XGHeaven and 1 more
The compiler will not add dependencies (i.e. import statements) that did not exist in the user code when it emits declarations.
The problem is that I don't always need the import statements in the code (obviously, since it works without --declarations), and including them is noisy, causing things like tslint to complain about the "unused import". I can see why you wouldn't want the compiler to add dependencies to emitted javascript, but what's the problem with adding them to emitting declarations?
Reacted by Bruno Jouhier, Santiago Aguiar, Offirmo, Frederick Fogerty, Tushar Mathur, spiffytech, Andrew Ross, Rico Rodriguez, Ian MacLeod, Daniil Demidovich and 35 more
feel free to log an issue to track this suggestion. the rational was imports are a declaration of dependency, and the compiler should not create one for you unless you instruct it to do.
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:
moduleC needs to have a hard dependency of moduleA and import ABC
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 do import.
In my example:
moduleA => redux
moduleB => redux-thunk
moduleC => custom code on top of redux-thunk
moduleD => some library
ABC => Dispatch interface in redux
Reacted by Offirmo, Frederick Fogerty, Huan Li, Ian MacLeod, Xananax, Brian Frichette, Dave Wasmer, Andžej Maciusovič, Mick Dekkers and Eric Chan
Reacted by Offirmo, Frederick Fogerty, Ian MacLeod, John Reilly, Lasana Murray, Mick Dekkers and Eric Chan
Please reopen this issue, the issues outlined by Homa Wong (@unional) are very realistic and this makes it very hard to add any intermediate/helper libs on top of libraries while using the same types as the original module we are re-exporting.
Reacted by Offirmo, Frederick Fogerty, Huan Li, Franklin Davenport, Andžej Maciusovič, Mick Dekkers, Daniel Hernandez and Thomas Anciaux
The problem with adding the import is that with noUnusedLocals the compiler will complain about the type not being used. I could add an explicit type annotation, but then I don't get inference. Example:
I would like to omit type annotation for ensureFetched
Reacted by Frederick Fogerty, estk, Danny Martini, maddie, Dylan Stewart, Abdullah A. Hassan, Brian Frichette, David Sheldrick, Gabriel Grinberg, Michael Nahkies and 20 more
Salim (@salim7) this slows down your compilation times (because you're recompiling a library that should've already been compiled) and forces you to use the least common denominator of strictness settings with the target library.
import * as Foo from "./Foo";
export class Bar {
baz = new Foo.Baz(); // Compiler forgot "Foo." prefix in the type, and throws this error, because "Baz" without perfix is not imported.
getBaz() { // All the same
return new Foo.Baz();
}
}
Solution is specify type explicit:
import * as Foo from "./Foo";
export class Bar {
baz: Foo.Baz = new Foo.Baz(); // ok
getBaz(): Foo.Baz { // ok
return new Foo.Baz();
}
}
Reacted by Jackson Delahunt, Denis Knaack and Sam Lin
Activity
janakerman commentedon Nov 18, 2015
Oh - if this isn't a suitable place to ask this question. Please let me know and I'd be happy to post it on another medium.
RyanCavanaugh commentedon Nov 18, 2015
FWIW you'll usually get faster answers on Stack Overflow, but we do field well-phrased questions here.
The problem here is that you're using the
--declarationflag, but have not provided a way for the compiler to do its job.When trying to emit
ModuleA.d.ts, the compiler needs to write an object type literal (e.g.{ moduleB: { classA: *mumble?* } }) representing the shape of the module. But there isn't a name in scope that refers directly toclassA, so it the type "cannot be named" and there's an error.If you add an
importofClassAtoModuleA.ts, the error should go away.janakerman commentedon Nov 23, 2015
Hi Ryan - thanks for the advice RE stack overflow and the advice - adding the import to
ModuleAgot things compiling as expected.A little context here - my goal here is indeed to generate declarations for my external modules. (Something I believe isn't quite working yet? #5039?) This import may be completely necessary but It feels a little strange for
ModuleAto need to import the symbols that moduleB already exports. The result is that every time I add toModuleB's object export, I'll need to add the import toModuleA- either directly:Or via
ModuleB(in an effort to reduce specifying paths forClassAin both places):I know very little about the TS compiler - so maybe what I'm saying is completely unrealistic, but at first thought it feels like the compiler could deduce that
ModuleBis exporting an object with N symbols on it, soModuleAneeds those symbols? The import information forModuleBis already there - couldModuleAuse that? Or would that be impossible because the import forClassAis completely internal toModuleB, so there's no way that the compiler can deduce the type forClassAwhen creating definitions forModuleA?Thank you again for taking the time to answer. I've sorted my problem now so the above is mainly curiosity - there's no rush to answer.
RyanCavanaugh commentedon Nov 23, 2015
I'm not sure exactly what you're saying. What should
ModuleA.d.tslook like in this proposal?mhegazy commentedon Dec 2, 2015
The compiler will not add dependencies (i.e. import statements) that did not exist in the user code when it emits declarations. the error you are getting means that the compiler is trying to write a type annotation for an exported declaration but could not. this can have one of two reasons, either the name is not accessible, i.e. not imported in the current module, or there is a declaration that is shadowing the original declaration.
in both cases, your work around is to add explicit type annotation, if you add any type annotation, it will be emitted verbatim in the output; the other option is to make sure the name is accessible, i.e. you have an import to the module, and you understand what that means for your users that are importing your module.
do not build description files
cwalv commentedon Jun 29, 2016
Mohamed Hegazy (@mhegazy) said:
The problem is that I don't always need the import statements in the code (obviously, since it works without
--declarations), and including them is noisy, causing things liketslintto complain about the "unused import". I can see why you wouldn't want the compiler to add dependencies to emitted javascript, but what's the problem with adding them to emitting declarations?mhegazy commentedon Jun 29, 2016
feel free to log an issue to track this suggestion. the rational was imports are a declaration of dependency, and the compiler should not create one for you unless you instruct it to do.
unional commentedon Jul 9, 2016
This could have deeper consequences.
Consider
moduleA->moduleB->moduleC->moduleD.moduleBis the one that needs to doimport { ABC } from 'moduleA'to get around this issue.When
moduleCusesmoduleBand export its signature, it again fails to compile becausemoduleBneedsABCfrommoduleAbut this timemoduleBdidn't export it.This means either:
moduleCneeds to have a hard dependency ofmoduleAand importABCmoduleBneeds to not just import but also re-exportABCand thenmoduleCimports it.If
moduleDdoes 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 usingtypingsand there is an issue blocking me so: typings/typings#625EDIT: I am able to reproduce it and indeed my
moduleDneeds to referencemoduleAto do import.In my example:
moduleA=>reduxmoduleB=>redux-thunkmoduleC=> custom code on top ofredux-thunkmoduleD=> some libraryABC=>Dispatchinterface inreduxOffirmo commentedon Sep 11, 2016
I have the same problem described by Homa Wong (@unional)
frederickfogerty commentedon Sep 27, 2016
Please reopen this issue, the issues outlined by Homa Wong (@unional) are very realistic and this makes it very hard to add any intermediate/helper libs on top of libraries while using the same types as the original module we are re-exporting.
mhegazy commentedon Sep 28, 2016
Issue #9944 tracks adding the import at the declaration emit phase.
unional commentedon Sep 28, 2016
Thank!
1 remaining item
felixfbecker commentedon Mar 20, 2017
The problem with adding the import is that with
noUnusedLocalsthe compiler will complain about the type not being used. I could add an explicit type annotation, but then I don't get inference. Example:I would like to omit type annotation for
ensureFetchedrohmanhm commentedon Jul 7, 2017
styled-components/styled-components#973
salim7 commentedon Oct 17, 2017
I found a workaround for this:
in tsconfig:
include: [ ..., "node_modules/@your_scope/your_library" ]good luck and have fun 😃
pelotom commentedon Oct 17, 2017
Salim (@salim7) this slows down your compilation times (because you're recompiling a library that should've already been compiled) and forces you to use the least common denominator of strictness settings with the target library.
PFight commentedon Oct 29, 2017
Mohamed Hegazy (@mhegazy) the problem has reproduced in next scenario:
Solution is specify type explicit:
mhegazy commentedon Oct 30, 2017
I can not get this to reproduce using the sample above. please file a new bug, and provide more context to be able to reproduce the issue.
fix(app): fix typescript warning
mohyeid commentedon Dec 10, 2017
Павел Богатырёв (@PFight) Thank you! That was it for me!
brunolemos commentedon Jan 15, 2018
I have just faced this issue when using:
The solution was to do this:
But this really shouldn't be necessary tbh.
yordis commentedon Jan 22, 2018
Did anyone figured out how to deal with
noUnusedLocals?pelotom commentedon Jan 22, 2018
Yordis Prieto (@yordis)
// @ts-ignoreyordis commentedon Jan 22, 2018
Tom Crockett (@pelotom) yeah this is completely my fault,
I was thinking on one thing and I wrote something else.
Did Typescript fixed the issue between
noUnusedLocalsand the declarations?AnyhowStep commentedon Feb 8, 2018
My current workaround, which feels like a total pain to me,
Avoids
noUnusedLocals, allows type inference for generic interfaces, too, where possible.Re-type `helper` from `@ember/component/helper` (#24986)