TypeScript Office - 命名空间与模块
# 命名空间与模块
这篇文章概述了在 TypeScript 中使用模块和命名空间来组织你的代码的各种方法。我们还将讨论一些关于如何使用命名空间和模块的高级话题,并解决在 TypeScript 中使用它们时的一些常见陷阱。
关于ES模块的更多信息,请参见 Modules 文档 (opens new window)。更多关于 TypeScript 命名空间的信息,请参见 Namespaces 文档 (opens new window)。
注意:在非常老的 TypeScript 版本中,命名空间被称为「内部模块」,这比 JavaScript 模块系统要早。
# 使用模块
模块可以包含代码和声明。
模块也依赖于模块加载器(如 CommonJs/Require.js)或支持 ES 模块的运行时间。模块提供了更好的代码重用,更强的隔离性和更好的捆绑工具支持。
同样值得注意的是,对于 Node.js 应用程序,模块是默认的,我们在现代代码中推荐模块而不是命名空间。
从 ECMAScript 2015 开始,模块是语言的原生部分,所有兼容的引擎实现都应该支持。因此,对于新项目,模块将是推荐的代码组织机制。
# 使用命名空间
命名空间是一种 TypeScript 特有的组织代码的方式。
命名空间是全局命名空间中简单命名的 JavaScript 对象。这使得命名空间的使用非常简单。与模块不同,它们可以跨越多个文件,并且可以使用 outFile 串联。命名空间可以成为 Web 应用程序中结构化代码的一个好方法,所有的依赖关系都包含在 HTML 页面的
就像所有的全局命名空间污染一样,可能很难识别组件的依赖关系,特别是在一个大型应用程序中。
# 命名空间和模块的陷阱
在本节中,我们将介绍使用命名空间和模块的各种常见陷阱,以及如何避免这些陷阱。
/// <reference>
为模块命名
一个常见的错误是试图使用 ///
语法来引用一个模块文件,而不是使用 import 语句。为了理解这种区别,我们首先需要理解编译器是如何根据 import 的路径(例如,在 import x from "...";
中的 ...
,import x = require("...");
等等)路径来定位模块的类型信息。
编译器将尝试找到一个 .ts
,.tsx
,然后是一个具有适当路径的 .d.ts
。如果找不到一个特定的文件,那么编译器将寻找一个环境模块声明。回顾一下,这些需要在 .d.ts
文件中声明。
- myModules.d.ts
// 在一个.d.ts文件或不是模块的.ts文件中
declare module "SomeModule" {
export function fn(): string;
}
2
3
4
- myOtherModule.ts
/// <reference path="myModules.d.ts" />
import * as m from "SomeModule";
2
这里的引用标签允许我们找到包含环境模块声明的声明文件。几个 TypeScript 样本使用的 node.d.ts
文件就是这样被消耗的。
# 不必要的命名方式
如果你要把一个程序从命名空间转换为模块,很容易就会出现一个看起来像这样的文件:
- shapes.ts
export namespace Shapes {
export class Triangle {
/* ... */
}
export class Square {
/* ... */
}
}
2
3
4
5
6
7
8
这里的顶层命名空间 Shapes 毫无理由地将 Triangle 和 Square 包裹起来。这让你的模块的使用者感到困惑和厌烦。
- shapeConsumer.ts
import * as shapes from "./shapes";
let t = new shapes.Shapes.Triangle(); // shapes.Shapes?
2
TypeScript 中模块的一个关键特征是,两个不同的模块永远不会将名字贡献给同一个范围。因为模块的消费者决定给它分配什么名字,所以不需要主动将导出的符号包裹在一个命名空间中。
重申一下为什么你不应该尝试对模块内容进行命名空间,命名空间的一般想法是提供结构体的逻辑分组,并防止名称碰撞。因为模块文件本身已经是一个逻辑分组,它的顶层名称由导入它的代码定义,所以没有必要为导出的对象使用一个额外的模块层。
下面是一个修改后的例子:
- shapes.ts
export class Triangle {
/* ... */
}
export class Square {
/* ... */
}
2
3
4
5
6
- shapeConsumer.ts
import * as shapes from "./shapes";
let t = new shapes.Triangle();
2
# 模块的权衡
就像 JS 文件和模块之间有一对一的对应关系一样,TypeScript 在模块源文件和其发射的 JS 文件之间有一对一的对应关系。这样做的一个影响是,根据你的目标模块系统,不可能串联多个模块源文件。例如,你不能在针对 commonjs 或 umd 时使用 outFile 选项,但在 TypeScript 1.8 及更高版本中,在针对 amd 或 system 时可以使用 outFile 。