You are not using modules. Modules have one or more top level import
or export
statements. Instead, you are using the global namespace and creating subnamespaces of the global namespace to organize your program. This is called the revealing module pattern. It does not involve using modules.
Unfortunately, TypeScript used to refer to this pattern as using Internal Modules. This terminology has since been deprecated and use of the module x {}
syntax (note the lack of "
s around x
) is strongly discouraged.
The language has introducing a synonymous keyword namespace
, to reduce confusion.
JavaScript loaders and bundlers like Webpack, RequireJS, and SystemJS work with modules, that is what TypeScript referred to as external modules.
To clarify the following constructs you mention are not module related
top level, non-exported module
/namespace
syntactic declarations
module vi.input.header { ... }
this would now be written as
namespace vi.input.header { ... }
in order to minimize confusion but, irregardless, the emit has always resulted in.
var vi;
(function (vi) {
var input;
(function (input) {
var header;
(function (header) {
})(header = input.header || (input.header = {}));
})(input = vi.input || (vi.input = {}));
})(vi || (vi = {}));
Note this mutates the global scope in a pattern commonly used by various libraries. namespace
s (formerly called internal modules) like the one above have the interesting property that multiple files can contribute to their contents, and this is in fact their primary purpose. This explains the degree of nesting and the conditional assignments to variables in the emit above. This has nothing to do with using Modules.
import
assignments that reference namespace
members such as
import IDirective = angular.IDirective;
do not qualify as top level import
declarations, and thus do not cause their containing file to be considered a module. This is true even if they are placed at the top level of a file. The reason is that module systems, be they AMD, CommonJS, System, or ES2015, all use strings as module specifiers, importing from such strings; which incidentally may represent file paths, urls, resolved simple names, or synthetic module ids.
Again, the import name = qualified.global.name
statements in your code are a TypeScript specific feature that is unrelated to Modules. They can be quite useful for aliasing nested types and values, but modules they do not make.
Now here is where it gets interesting, and where it intersects with your specific question namespace
s can be used, and at times quite elegantly, from within external modules, but their semantics are very different
Consider
services.ts
export namespace app {
export class SomeService { }
}
which compiles into the following JavaScript
export var app;
(function (app) {
class SomeService {
}
app.SomeService = SomeService;
})(app || (app = {}));
main.ts
export namespace app {
export function bootstrap() { }
}
which compiles into the following JavaScript
export var app;
(function (app) {
function bootstrap() { }
app.bootstrap = bootstrap;
})(app || (app = {}));
Both of above are external modules, that is true modules, that use namespaces as internal code organization mechanisms but they key takeaway is that they do not contribute to a shared app
namespace, each having their own, file scoped app
variable. Neither has implicit access to the other's members, the declarations of namespace app
do not merge across files, and their having a similar internal naming scheme is incidental to their modularity.
So how does all of this relate to your question and to the suggestions you tried to apply?
Let us see
headers.ts
module vi.input.header {
import IDirective = angular.IDirective;
export class VIInputDirective implements IDirective {
static whatever() { }
}
}
This file is not a module, as explain above, and uses the global namespace to expose its declarations. If we were writing this today, we would use the namespace
keyword instead of the module
keyword, by convention.
main.ts
import {VIInputDirective} from "./header.ts"; // Nothing imported; Cannot Resolve issue
VIInputDirective.whatever(); // Does not work
Indeed neither line works because you are importing headers.ts as if it were a module, but as we have just seen it is not a module. Furthermore, the first line, which is a top level import
statement that imports from a module specifier string, ironically makes main.ts, itself, a module.
In short the two styles do not mix well, and sharing code between them is not simple and is not something you should likely try to do (I have left the UMD format out of this answer to try to keep it relatively straightforward).
Now we come full circle
strong text
declare module vi.input {
....
}
If I swap import {VIInputDirective} from "./header.ts"; in the main.ts file with import VIMHeaderDirective = vi.input.header.VIInputDirective; it works fine, but then webpack on transpile/inject gives me the following error:
Indeed as explained above. This import
, which does not target a module specifier string, and the absence of any others top level imports or exports, changes main.ts such that it is no longer a module. This causes TypeScript to TypeCheck it correctly, global variables referencing each other using import = namespace.value
is perfectly legitimate, but these are not modules and JavaScript tools, such as Webpack, operate on modules.
So how would you write this app in this brave new world?
Since you are using a module bundling tool, Webpack, you would write it using proper modules all the way down.
main.ts
import {VIInputDirective} from "./header.ts";
VIInputDirective.whatever();
headers.ts
import {IDirective} from 'angular';
export class VIInputDirective implements IDirective {
static whatever() { }
}
material.d.ts no longer looks as it did when you wrote this, as it has been updated to work with proper modules. If you need to reference something from it, use the module syntax
my-dialog-options.ts
import {material} from 'angular';
const dialogOptions: material.IDialogOptions = { ... };
export default dialogOptions;
I have tried not to oversimplify but some hand waving has been necessary to avoid writing a novella on the subject, but I believe I hope to have covered and conveyed the key points.