# 命名空间和模块
这篇文章概述了在 TypeScript 中使用模块和命名空间来组织代码的各种方法。我们还将讨论如何使用命名空间和模块的一些高级主题,并解决在 TypeScript 中使用它们时的一些常见缺陷。
有关 ES 模块的更多信息,请参阅 模块
文档。有关 TypeScript 命名空间的更多信息,请参阅 命名空间
文档。
注意:在非常旧的 TypeScript 版本中,命名空间被称为 'Internal Modules',这些早于 JavaScript 模块系统。
# 使用模块
模块可以包含代码和声明。
模块还依赖于模块加载器(例如 CommonJs/Require.js)或支持 ES 模块的运行时。模块为捆绑提供了更好的代码重用、更强的隔离和更好的工具支持。
还值得注意的是,对于 Node.js 应用程序,模块是默认的,我们建议在现代代码中使用模块而不是命名空间。
从 ECMAScript 2015 开始,模块是语言的本机部分,所有兼容的引擎实现都应支持。因此,对于新项目,模块将是推荐的代码组织机制。
# 使用命名空间
命名空间是一种特定于 TypeScript 的代码组织方式。命名空间只是全局命名空间中命名的 JavaScript 对象。这使得命名空间成为一个非常简单易用的构造。与模块不同,它们可以跨越多个文件,并且可以使用 outFile
连接。命名空间是在 Web 应用程序中构建代码的好方法,所有依赖项都作为 <script>
标记包含在 HTML 页面中。
就像所有全局命名空间污染一样,很难识别组件依赖关系,尤其是在大型应用程序中。
# 命名空间和模块的陷阱
在本节中,我们将描述使用命名空间和模块的各种常见陷阱,以及如何避免它们。
# /// <reference>
-ing 一个模块
一个常见的错误是尝试使用 /// <reference ... />
语法来引用模块文件,而不是使用 import
语句。要理解这种区别,我们首先需要了解编译器如何根据import
的路径(例如import x from "...";
、import x = require("...");
中的...
等)路径来定位模块的类型信息。
编译器将尝试找到具有适当路径的 .ts
、.tsx
和 .d.ts
。如果找不到特定文件,则编译器将查找环境模块声明。回想一下,这些需要在 .d.ts
文件中声明。
myModules.d.ts
// In a .d.ts file or .ts file that is not a module:
declare module "SomeModule" {
export function fn(): string;
}
myOtherModule.ts
/// <reference path="myModules.d.ts" />
import * as m from "SomeModule";
这里的引用标签允许我们找到包含环境模块声明的声明文件。这就是使用几个 TypeScript 示例使用的 node.d.ts
文件的方式。
# 不必要的命名空间
如果您要将程序从命名空间转换为模块,很容易得到如下所示的文件:
shapes.ts
export namespace Shapes {
export class Triangle {
/* ... */
}
export class Square {
/* ... */
}
}
这里的顶级命名空间 Shapes
无缘无故地包装了 Triangle
和 Square
。这对于您的模块的使用者来说是令人困惑和烦人的:
shapeConsumer.ts
import * as shapes from "./shapes";
let t = new shapes.Shapes.Triangle(); // shapes.Shapes?
TypeScript 中模块的一个关键特性是两个不同的模块永远不会将名称贡献给同一作用域。因为模块的使用者决定为其分配什么名称,所以无需主动将导出的符号包装在命名空间中。
重申为什么不应该尝试为模块内容命名,命名空间的一般思想是提供构造的逻辑分组并防止名称冲突。因为模块文件本身已经是一个逻辑分组,并且它的顶级名称是由导入它的代码定义的,所以没有必要为导出的对象使用额外的模块层。
这是一个修改后的例子:
shapes.ts
export class Triangle {
/* ... */
}
export class Square {
/* ... */
}
shapeConsumer.ts
import * as shapes from "./shapes";
let t = new shapes.Triangle();
# 模块的权衡
就像 JS 文件和模块之间是一一对应的一样,TypeScript 在模块源文件和它们发出的 JS 文件之间也是一一对应的。这样做的一个影响是,不可能根据您的目标模块系统连接多个模块源文件。例如,您不能在定位 commonjs
或 umd
时使用 outFile
选项,但在 TypeScript 1.8 及更高版本中,这是可能的在定位 amd
或 system
时使用 outFile
。