# 命名空间
# 命名空间:namespace
命名空间一般用来定义对象(需结合declare)。使用命名空间的好处是,可以自定义对外可见/不可见的的类型或值。极大的规避了全局命名冲突的问题。同时我们可以使用 export 来对外暴露命名空间内的值或类型。
在代码量较大的情况下,为了避免命名空间冲突,可以将相似的函数、类、接口防止在命名空间内。
命名空间可以将代码包裹起来,只对外暴露需要在外部访问的对象,命名空间内通过 export 向外导出。
命名空间是内部模块,主要用于组织代码,避免命名冲突。
# namespace 官方示例
namespace Validation {
// 通过export暴露的类型/值,可在namespaace外部访问
// 暴露一个interface,其包含一个方法签名
export interface StringValidator {
isAcceptable(s: string): boolean;
}
// 没有使用export来导出,只能在namespace内部访问
const lettersRegexp = /^[A-Za-z]+$/;
const numberRegexp = /^[0-9]+$/;
// 暴露两个class
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
}
// 测试用例
let strings = ["Hello", "98052", "101"];
// 字符串索引签名,初始化变量
let validators: { [s: string]: Validation.StringValidator } = {};
// 添加两个属性,分别是一个validator实例
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();
// 开始测试
for (let s of strings) {
for (let name in validators) {
console.log(
`"${s}" - ${
validators[name].isAcceptable(s) ? "matches" : "does not match"
} ${name}`
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
可以发现,我们可以在 namespace 中自由定义类型或者值,只要不导出,它就是私有的。
# 多命名空间文件
namespace 的声明是可以合并的,那么这就使得多文件命名空间成为了可能。但一个项目变得很大时,namespace 也会变得很大,将所有内容都写在一个 namespace 文件中就会变得不太明智。因此 TS 支持我们将 namespace 进行模块化,将内容拆分到多个文件中。
模块化:通过三斜杠指令:/// <reference path="xxx" />
来指定路径,告诉编译器文件之间的依赖关系,注意 三斜杠指令应位于文件的开头。
(其实三斜杠指令不仅可以运用在命名空间,环境声明:declare 等同样可以)
以上面的官方示例为例进行拆分:
1.主体
Validation.js:
namespace Validation{
export interface StringValidator {
isAcceptable(s: string): boolean;
}
}
2
3
4
5
2.扩展字母校验器
LettersOnlyValidator.js:
// 首先通过三斜杠指令来引入需要合并的命名空间
/// <reference path="Validation.ts" />
namespace Validation{
// 私有成员,仅在当前namaspace中可见
const lettersRegexp = /^[A-Za-z]+$/;
// 导出共享成员
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
3.继续扩展
ZipCodeValidator.js:
/// <reference path="Validation.ts" />
namespace Validation{
// 私有成员,仅在当前namaspace中可见
const numberRegexp = /^[0-9]+$/;
// 导出共享成员
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
}
2
3
4
5
6
7
8
9
10
11
4.使用
笔记
合并命名空间后,如果想要在其他文件中使用对应的命名空间模块,仍然需要使用三斜杆指令来引入相关的 namespace 文件。
test.ts:
// 使用三斜杠指令来引入相关的namespace文件
/// <reference path="Validation.ts" />
/// <reference path="LettersOnlyValidator.ts" />
/// <reference path="ZipCodeValidator.ts" />
// 以下用法和在最开始没有拆分使相同
// 测试用例
let strings = ["Hello", "98052", "101"];
// 变量声明,索引签名
let validators: { [s: string]: Validation.StringValidator } = {};
// 添加validator实例
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();
// 开始测试
for (let s of strings) {
for (let name in validators) {
console.log(
`"${s}" - ${
validators[name].isAcceptable(s) ? "matches" : "does not match"
} ${name}`
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 别名
可以通过给命名空间设置别名的方式来简化引用。
// 官方示例的第一个嵌套namespace
namespace Shapes {
const age = 18
export const getAge = () => age
export namespace Polygons {
export class Triangle {}
export class Square {}
}
}
// 可以给一般成员起别名
import age = Shapes.getAge
// 也可以给嵌套的namespace起别名
import polygons = Shapes.Polygons;
let sq = new polygons.Square(); // 和 'new Shapes.Polygons.Square()' 一样
2
3
4
5
6
7
8
9
10
11
12
13
14
注意
注意,这不要和模块的导入语法import q = require(X)
相混淆,这俩不是同一个东西。命名空间的import newName = X.y.x
只是单纯地给命名空间的成员起一个别名而已。
# 环境命名空间(declare)
其中涉及一个名词:ambient,我们通常将那些还没有定义的声明叫做 ambient,这些声明往往出现在 .d.ts 的拓展文件中,因为有些库并不是使用 TS 写的,而是使用 JS 写的,而大多数JS
库所暴露的都是一个顶级的object
,为了让这些库中暴露的 API 能够支持 TS,我们可以用环境命名空间来定义这些第三方库的形状。
// 注意 '环境' 的声明都需要添加declare关键字
// 以D3库为例,声明没有定义实现的接口
declare namespace D3 {
export interface Selectors {
select: {
(selector: string): Selection;
(element: EventTarget): Selection;
};
}
export interface Event {
x: number;
y: number;
}
export interface Base extends Selectors {
event: Event;
}
}
// 声明没有分配的值
declare var d3: D3.Base;
/* d3的形状是
{
select: {
(selector: string): Selection;
(element: EventTarget): Selection;
};
event: Event;
}
*/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28