Skip to main content

模块与命名空间

全局模块

在默认情况下,当你开始在一个新的 TypeScript 文件中写下代码时,它处于全局命名空间中。如在 foo.ts 里的以下代码。

foo.ts
const foo = 123;

如果在相同的工程下,我们再建立一个新的文件 bar.ts,写如下代码就会报错:

bar.ts
const foo = 456; // ERROR: 无法重新声明块范围变量“foo”。

虽然在两个不同的文件内,但是它们所处的空间是全局的。

使用全局变量空间是危险的,因为它会与其他文件内的代码命名冲突。所以需要引入模块系统来规避这个情况。

文件模块

TypeScript 与 ECMAScript 2015 一样,任何包含顶级 import 或者 export 的文件都被当成一个模块。

相反地,如果一个文件不带有顶级的import或者export声明,那么它的内容被视为全局可见的。

bar.ts 文件中,改造一下上面的代码,如下:

bar.ts
export const foo = 456;

之前的问题就消失了,原因是 export 把变量 foo 变成了局部的命名空间内,与 foo.ts 文件中的全局变量 foo 不再产生冲突。

tip

推荐使用 module: commonjs 选项以及使用 ES 模块语法导入、导出、编写模块。

如果你需要使用 moduleResolution: node 选项,你应该将此选项放入你的配置文件中。如果你使用了 module: commonjs 选项, moduleResolution: node 将会默认开启。

命名空间

在 JavaScript 使用命名空间时,这有一个常用的、方便的语法:

(function (something) {
something.foo = 123;
})(something || (something = {}));

something || (something = {}) 允许匿名函数 function (something) {} 向现有对象添加内容,或者创建一个新对象,然后向该对象添加内容。这意味着你可以拥有两个由某些边界拆成的块。

(function(something) {
something.foo = 123;
})(something || (something = {}));

console.log(something);
// { foo: 123 }

(function(something) {
something.bar = 456;
})(something || (something = {}));

console.log(something); // { foo: 123, bar: 456 }

在确保创建的变量不会泄漏至全局命名空间时,这种方式在 JavaScript 中很常见。因此 TypeScript 提供了 namespace 关键字来描述这种分组,如下所示。

namespace Utility {
export function log(msg: string) {
console.log(msg);
}
export function error(msg: string) {
console.log(msg);
}
}

Utility.log('Call me');
Utility.error('maybe');

编译成:

"use strict";
var Utility;
(function (Utility) {
function log(msg) {
console.log(msg);
}
Utility.log = log;
function error(msg) {
console.log(msg);
}
Utility.error = error;
})(Utility || (Utility = {}));
Utility.log('Call me');
Utility.error('maybe');

命名空间定义了标识符的可见范围,一个标识符可在多个名字空间中定义,它在不同名字空间中的含义是互不相干的。

如果我们需要在外部可以调用,则需要在命名空间中使用 export 关键字进行导出.

命名空间的用处

命名空间在现代 TypeScript 项目开发中的重要性并不高,主要原因是 ES6 引入了模块系统,文件即模块的方式使得开发者能更好的得组织代码,但是命名空间并非一无是处,通常会在一些非 TypeScript 原生代码的 .d.ts 文件中使用。

因此在正常的 TypeScript 项目开发过程中并不建议用命名空间,而是建议使用文件模块。

global.d.ts

在上文中,比较了全局变量与文件模块,并且我们推荐使用基于文件的模块,而不是选择污染全局命名空间。

然而,如果你的团队里有 TypeScript 初学者,你可以提供他们一个 global.d.ts 文件,用来将一些接口或者类型放入全局命名空间里,这些定义的接口和类型能在你的所有 TypeScript 代码里使用。

参考文档

深入理解TypeScript