您没有使用模块。模块具有一个或多个顶层
import
或
export
语句。相反,您正在使用全局命名空间并创建全局命名空间的子命名空间来组织程序。这被称为揭示模块模式。它不涉及使用模块。
不幸的是,TypeScript 曾经将此模式称为使用
内部模块。这种术语已经被弃用,并且强烈不建议使用
module x {}
语法(注意
x
周围缺少
"
)。该语言引入了一个同义词关键字
namespace
,以减少混淆。
JavaScript 加载器和打包工具,如 Webpack、RequireJS 和 SystemJS,可以处理模块,这就是 TypeScript 所谓的
外部模块。
为了澄清,您提到的以下结构与模块无关。
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.
再次强调,代码中的import name = qualified.global.name
语句是TypeScript特有的功能,与模块无关。它们非常有用,可以为嵌套类型和值取别名,但并不能创建模块。
现在我们来谈谈有趣的部分,也是与您的具体问题相关的部分: namespace
可以被使用,并且有时候在外部模块中使用起来非常优雅,但它们的语义是非常不同的。
请考虑以下内容:
services.ts
export namespace app {
export class SomeService { }
}
这将编译成以下JavaScript代码:
export var app;
(function (app) {
class SomeService {
}
app.SomeService = SomeService;
})(app || (app = {}));
main.ts
export namespace app {
export function bootstrap() { }
}
这将编译成以下JavaScript代码:
export var app;
(function (app) {
function bootstrap() { }
app.bootstrap = bootstrap;
})(app || (app = {}));
上述两个都是外部模块,也就是真正的模块,它们使用命名空间作为内部代码组织机制,但关键是它们
不会对
共享的app
命名空间造成影响,每个模块都有自己的文件范围内的
app
变量。它们都不能隐式访问对方的成员,
namespace app
的声明不会跨文件合并,它们具有相似的内部命名方案只是模块化的副作用。
那么这一切与你的问题和你尝试应用的建议有什么关系呢?
我们来看看
headers.ts
module vi.input.header {
import IDirective = angular.IDirective;
export class VIInputDirective implements IDirective {
static whatever() { }
}
}
如上所述,此文件不是一个模块,而是使用全局命名空间来公开其声明。如果我们今天编写这个文件,我们会使用namespace
关键字而不是module
关键字,按照惯例。
main.ts
import {VIInputDirective} from "./header.ts"
VIInputDirective.whatever()
事实上,这两种方式都不起作用,因为你像导入模块一样导入了
headers.ts,但正如我们刚刚看到的,它并不是一个模块。此外,第一行代码是一个顶级
import
语句,它从一个模块指定字符串中导入,具有讽刺意味的是,它使得
main.ts 本身成为了一个模块。
简而言之,这两种风格不太相容,并且在它们之间共享代码并不简单,也不是你应该尝试做的事情(为了保持相对简单,我已经忽略了UMD格式)。
现在我们来到了完整的循环。
强调文本
declare module vi.input {
....
}
如果我在main.ts文件中使用import VIMHeaderDirective = vi.input.header.VIInputDirective;替换import {VIInputDirective} from "./header.ts";,那么它可以正常工作,但是当转译/注入时,webpack会给出以下错误提示:
正如上面所解释的那样,这个import并没有针对一个模块指定符字符串,而且在任何其他顶级导入或导出的情况下都不存在,改变了main.ts文件,使其不再是一个模块。这会导致TypeScript正确地进行类型检查,使用import = namespace.value互相引用全局变量是完全合法的,但这些不是模块,JavaScript工具(比如Webpack)操作的是模块。
那么,在这个崭新的世界里,你将如何编写这个应用程序?既然您正在使用一个模块捆绑工具Webpack,那么您应该一直使用适当的模块来编写它。
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文件现在与您编写时的不同,因为它已更新为使用正确的模块。如果您需要引用其中的内容,请使用模块语法。
my-dialog-options.ts
import {material} from 'angular';
const dialogOptions: material.IDialogOptions = { ... };
export default dialogOptions;
我尽力避免过于简化,但为了避免写一篇长篇小说,有些手势必不可少。我相信我已经涵盖并传达了关键要点。
import * as x from "./header"
然后创建new x.vi.input.header.VIInputDirective
吗? - Banners