总结
- 类型
- 在模块模式中不推荐使用,只能配合declare
用于声明外部模块注册的全局变量的类型。
- 命名空间可以做为 tsconfig 的配置目标,指定某些类型检查在指定命名空间中进行。
- 当这个命名空间内没有任何实现时,该命名空间作为纯类型使用。 - 实现
- 命名空间在单上下文模式中用于创建全局对象,通过/// <reference path="Validation.ts" />
引入。需要export
语句标明该对象外部可以访问的属性,不能使用export default
-import q = x.y.z
用于给命名空间内的导出创建别名,该别名的指向和命名空间的导出的实际指向是互相隔离的(未验证) - 特点
- 命名空间外部没有导入导出,默认是全局注册的。例如后文中declare namespace D3
声明一个全局对象 D3 的存在。
原文
原文地址 www.tslang.cn
第一步
下面的例子里,把所有与验证器相关的类型都放到一个叫做Validation
的命名空间里。 因为我们想让这些接口和类在命名空间之外也是可访问的,所以需要使用 export
。 相反的,变量 lettersRegexp
和numberRegexp
是实现的细节,不需要导出,因此它们在命名空间外是不能访问的。 在文件末尾的测试代码里,由于是在命名空间之外访问,因此需要限定类型的名称,比如 Validation.LettersOnlyValidator
。
使用命名空间的验证器
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
const lettersRegexp = /^[A-Za-z]+$/;
const numberRegexp = /^[0-9]+$/;
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);
}
}
}
// Some samples to try
let strings = ["Hello", "98052", "101"];
// Validators to use
let validators: { [s: string]: Validation.StringValidator; } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();
// Show whether each string passed each validator
for (let s of strings) {
for (let name in validators) {
console.log(`"${ s }" - ${ validators[name].isAcceptable(s) ? "matches" : "does not match" } ${ name }`);
}
}
分离到多文件
多文件中的命名空间
现在,我们把Validation
命名空间分割成多个文件。 尽管是不同的文件,它们仍是同一个命名空间,并且在使用的时候就如同它们在一个文件中定义的一样。 因为不同文件之间存在依赖关系,所以我们加入了引用标签来告诉编译器文件之间的关联。 我们的测试代码保持不变。
Validation.ts
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
}
LettersOnlyValidator.ts
/// <reference path="Validation.ts" />
namespace Validation {
const lettersRegexp = /^[A-Za-z]+$/;
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
}
ZipCodeValidator.ts
/// <reference path="Validation.ts" />
namespace Validation {
const numberRegexp = /^[0-9]+$/;
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
}
Test.ts
/// <reference path="Validation.ts" />
/// <reference path="LettersOnlyValidator.ts" />
/// <reference path="ZipCodeValidator.ts" />
// Some samples to try
let strings = ["Hello", "98052", "101"];
// Validators to use
let validators: { [s: string]: Validation.StringValidator; } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();
// Show whether each string passed each validator
for (let s of strings) {
for (let name in validators) {
console.log(`"${ s }" - ${ validators[name].isAcceptable(s) ? "matches" : "does not match" } ${ name }`);
}
}
注释:以下是打包命令
当涉及到多文件时,我们必须确保所有编译后的代码都被加载了。 我们有两种方式。
第一种方式,把所有的输入文件编译为一个输出文件,需要使用--outFile
标记:
注释:
--outFile
是指根据///<reference``>
和import
的顺序,把文件打包成一个文件。
tsc --outFile sample.js Test.ts
编译器会根据源码里的引用标签自动地对输出进行排序。你也可以单独地指定每个文件。
tsc --outFile sample.js Validation.ts LettersOnlyValidator.ts ZipCodeValidator.ts Test.ts
第二种方式,我们可以编译每一个文件(默认方式),那么每个源文件都会对应生成一个 JavaScript 文件。 然后,在页面上通过 <script>
标签把所有生成的 JavaScript 文件按正确的顺序引进来,比如:
注释:现代浏览器会并行加载js文件,但是按照书写顺序执行代码。加载或者执行 js 时会阻塞对标签的解析,也就是阻塞了dom树的形成。
MyTestPage.html (excerpt)
<script src="Validation.js" type="text/javascript" />
<script src="LettersOnlyValidator.js" type="text/javascript" />
<script src="ZipCodeValidator.js" type="text/javascript" />
<script src="Test.js" type="text/javascript" />
别名
另一种简化命名空间操作的方法是使用import q = x.y.z
给常用的对象起一个短的名字。 不要与用来加载模块的 import x = require('name')
语法弄混了,这里的语法是为指定的符号创建一个别名。 你可以用这种方法为任意标识符创建别名,也包括导入的模块中的对象。
namespace Shapes {
export namespace Polygons {
export class Triangle { }
export class Square { }
}
}
import polygons = Shapes.Polygons;
let sq = new polygons.Square(); // Same as "new Shapes.Polygons.Square()"
注释:
import q = x.y.z
可以用于给命名空间本身或内部的类型给予别名,如果导入的是实体值,那么这个值是导入的一个拷贝,他们彼此互不影响?
注意,我们并没有使用require
关键字,而是直接使用导入符号的限定名赋值。 这与使用 var
相似,但它还适用于类型和导入的具有命名空间含义的符号。 重要的是,对于值来讲, import
会生成与原始符号不同的引用,所以改变别名的var
值并不会影响原始变量的值。
使用其它的JavaScript库
为了描述不是用 TypeScript 编写的类库的类型,我们需要声明类库导出的 API。 由于大部分程序库只提供少数的顶级对象,命名空间是用来表示它们的一个好办法。
外部命名空间
流行的程序库 D3 在全局对象d3
里定义它的功能。 因为这个库通过一个 <script>
标签加载(不是通过模块加载器),它的声明文件使用内部模块来定义它的类型。 为了让 TypeScript 编译器识别它的类型,我们使用外部命名空间声明。 比如,我们可以像下面这样写:
D3.d.ts (部分摘录)
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;