Repository navigation
Use external module in internal modules #557
Description
Hi,
I don't have a strong background in JavaScript, I have mainly been coding in C#.
I'm starting a client-side application using TypeScript and Angular.js and wish to use the underscore.string library, with its TS definition available on NuGet.
Here is an excerpt of that definition:
declare module 'underscore.string' {
var underscoreString: UnderscoreStringStatic;
export = underscoreString;
}Here are my observations and assumptions:
-
The definition contains
declare module 'underscore.string'. The quoted name automatically makes it an external module. -
Had it been
declare module underscoreString, it would still have been an external module because of its use ofexport =. -
If I write
import _ = require('underscore.string')in the same file as an internal module (but not within the module), that internal module has to become external. Besides an error appears:Cannot compile external modules unless the '--module' flag is provided.(Which, in a VS project, means having to check either AMD or CommonJS in the project properties) -
If I try
import _ = require('underscore.string')within an internal module, that internal module also has to become external, otherwise the following error appears:Import declarations in an internal module cannot reference an external module. -
The language specification for v1.0 mentions an ImportDeclaration in section 10.3:
import Identifier = EntityName;
However this only applies to internal modules, which 'underscore.string' is not if my first assumptions are correct.
-
If I change my internal modules to be external, they won't be open-ended anymore and I will need to name a different module for each of my files.
-
I do not have difficulty using Angular.js because it was declared as an internal module.
Now, my main question is:
How can I use what appears to be an external module ('underscore.string') in my open-ended internal modules?
If I can't, then my corollary question is:
What is the most convenient way to structure the application without having to declare a new external module for each file? Especially since I would only name those modules after the sole class they contain (I follow C# convention of 1 class = 1 file).
Ideally I would deal with modules as I deal with .NET namespaces, but the way it looks now, I would have to deal with them as if every file were a .NET assembly.
Thanks in advance for any clarification
Activity
koloboid commentedon Aug 28, 2014
look at #17 - does is about the same?
antoinebj commentedon Aug 28, 2014
Well this is more of a question than a feature request or a bug report. I'm simply trying to understand and discuss it.
Regarding #17 , that still pertains to external modules. I would like to avoid having to make my internal modules external. Hence the question of whether that's feasible in that context, and if not, what the best workaround not involving a future release would be.
RyanCavanaugh commentedon Aug 28, 2014
This .d.ts file was written in the good "internal/external" pattern whereby you can consume it simply by referencing the .d.ts file and adding this line of code somewhere in your file:
Closing, but feel free to post follow-up questions.
ylemoigne commentedon Sep 27, 2015
Even if it's old, the title is exactly what i'm looking for. I look at #17 and i'm not sure it's the same issue. And as there is no clear answer, I'll try to explain the question clearly.
I want have an app.tsx which looks like this :
I want to organise the code, mainly by spliting the file and get
Fooin theOthermodule, so following ts handbook, I create a new fileFoo.tsx:Now, the
app.tsxlooks like :As I understand things, Other.Foo don't work because in Foo.tsx, there is an import of an external module.
So going back to the title of the question : How can I use external module in internal modules ?
weswigham commentedon Sep 27, 2015
Change
module Othertoexport module OtherinFoo.tsx, and addimport {Other} from "./Foo"in app.tsx.ylemoigne commentedon Sep 28, 2015
Great ! thanks Wesley Wigham (@weswigham) .
ylemoigne commentedon Sep 28, 2015
Ho no... Sorry, it still not good.
It works for one file only. As soon as I have a second file and I want it to contribute to the same module, I can't use
importtwice with same identifier in theapp.tsxweswigham commentedon Sep 28, 2015
Namespace merging is not a feature you can use with external modules. Instead of using namespaces to create a group of methods, use a file instead. For example, assume I have a foo.ts and a bar.ts:
If I want to access both of those through one object, I can create a third file, api.ts:
And now I can
import * as api from "./api"to get a single object with all the exported members of both foo and bar on them.Namespaces I find aren't actually terribly useful with external modules, except for singletons and file-internal organization.
davidstellini commentedon Feb 23, 2016
Wesley Wigham (@weswigham) Sure, and then you pollute the global scope by adding a gazilion different exports to it.
Internal modules allow me to split up my code and organize it nicely, but I can't use, or reference, an external module from my internal module?
weswigham commentedon Feb 23, 2016
How do external modules "pollute" anything? You can't even easily declare globals when using external modules, because everything gets module-scoped.
davidstellini commentedon Feb 23, 2016
I thought you meant:
export function foo() {}not
In the first case, isn't foo living in the window object? In either case; what this boils down to is something very simple: If you want to use separate your code using internal modules, then you effectively can't use third party libraries. Take a look at: http://stackoverflow.com/questions/35582275/using-a-node-library-within-a-typescript-internal-module
Referencing a gazilion different files via a relative path is ugly.
weswigham commentedon Feb 23, 2016
The first case does not put anything on the window object - it generates a module closure (depending on the module format) and adds that function to the exports of that closure.
That stack overflow post is shallow in its analysis (and is also an unanswered question) - any namespace structure can be replicated with external modules - look at tslint as a great example both of how to migrate from namespaces to external modules (see the code before v3, then after v3) and of how you can use external modules to layer APIs in this way to imitate namespace structure.
While you can reference everything directly, as done in that SO post, that can be indicative of poor API design, as that's akin to reaching into the "internal" API of every module you use, rather than organizing things into groups (API layers), then importing from that group (API). When done correctly, you should be able to pull in most of what a given file needs via a single import. Angular is actually structured very well internally, in this regard.
davidstellini commentedon Feb 23, 2016
Thank you for your suggestions, but you are basically saying:
I think you are right 95% of the time. In my specific use case, I have typescript files (Models) that are going to be generated by an external tool. Rather than using imports for cross-dependencies between model files, a "namespace Models" before every class file would have been perfect and reduced code-generation complexity.
Converting to external modules would not solve the problem, because external modules do not support namespace merging.
So I cannot, say, have something like:
I have to now import "Address" manually in UserModel. Is there any alternative?
weswigham commentedon Feb 23, 2016
UserModel.ts:AddressModel.ts:Model.ts:index.ts:You can also destructure only the members you need out of the import statement, which, IMO, usually makes it more clear what other bits the component depends upon.
Using them from another part of your program (assuming these are all in a
modelsfolder):Let me also clear something up for you which you example make it seem like you may be confusing:
importstatements for module systems and merged namespaces across files are fundamentally incompatible.namespaceswork around the concept that eachnamespaceis just an object with an initializing closure - merged namespaces just reinitialize the object with another closure to add more members. This all has to be done within the same scope so that global namespace object is available. The module systems you use withimportstatements have different scope rules - each file has its own scope, meaningnamespacescan't possibly merge across module boundaries, since they do not share a scope (and so end up initializing different objects). They are two, different, mutually exclusive ways of structuring a program. If you needrequirecalls in a program structured withnamespaces, write them asconst lib: LibType = require("lib"); // This means the compiler doesn't find the types for a library for you, you have to add them yourself, as addingimportorexportstatements make your project into modules, which then grants each module its own scope, making it pretty much impossible to usenamespaces as you would apparently like to.RyanCavanaugh commentedon Feb 23, 2016
Probably worth linking this, as usual: http://stackoverflow.com/questions/30357634/how-do-i-use-namespaces-with-typescript-external-modules
davidstellini commentedon Feb 23, 2016
That makes sense. I think the confusion stems from comparing namespaces to C# namespaces (the 'as usual' makes me think I'm not the only one). Intuitively, if you were a C# developer, your brain says, this is the equivalent of using an external DLL/Library (external module) in C#, and the compiler says you cannot use namespaces (internal module) in the same file - why is this limitation in place?!. That said, it is purely my confusion and I should be comparing with Javascript not C#. Thanks for clearing this up!