Typescript 命名空间是一种特殊的语法,它可以帮助我们将代码分割成不同的模块,从而使代码更加有条理、易于维护。它可以让我们在一个文件中定义多个命名空间,并且可以在不同的文件中使用这些命名空间。
Typescript 命名空间的定义方式有两种:内部模块和外部模块。内部模块是在一个文件中定义的命名空间,而外部模块是在不同文件中定义的命名空间。
// 内部模块 namespace MyNameSpace { export class MyClass { } } // 外部模块 export namespace MyNameSpace { export class MyClass { } }
Typescript 命名空间可以帮助我们解决具有相似功能的函数、变量、对象之间的命名冲突。例如,如果你想要创建一个具有相似功能的函数,但是函数的名字已存在,那么你就可以将这些函数分配到不同的命名空间中,这样就可以避免命名冲突。
// 创建一个函数 function add(x: number, y: number): number { return x + y; } // 创建一个具有相似功能的函数 namespace MathOperation { export function add(x: number, y: number): number { return x + y; } }
此外,Typescript 还允许我们使用三斜杠引用来引用外部文件中定义的命名空间。例如:
/// <reference path="MyNameSpace.ts" />
通过使用 Typescript 的命名空间特性,我们可以将代码分割成不同的模块,并避免代码之间出现重复、冲突、耦合性高之情况。因此 Typescript 的命名空间特性对于大型项目来说是非常重要的。
关于术语的一点说明: 请务必注意一点,TypeScript 1.5里术语名已经发生了变化。 “内部模块”现在称做“命名空间”。 “外部模块”现在则简称为“模块”,这是为了与 ECMAScript 2015里的术语保持一致,(也就是说
module X {
相当于现在推荐的写法namespace X {
)。
这篇文章描述了如何在TypeScript里使用命名空间(之前叫做“内部模块”)来组织你的代码。
就像我们在术语说明里提到的那样,“内部模块”现在叫做“命名空间”。
另外,任何使用module
关键字来声明一个内部模块的地方都应该使用namespace
关键字来替换。
这就避免了让新的使用者被相似的名称所迷惑。
我们先来写一段程序并将在整篇文章中都使用这个例子。 我们定义几个简单的字符串验证器,假设你会使用它们来验证表单里的用户输入或验证外部数据。
interface StringValidator {
isAcceptable(s: string): boolean;
}
let lettersRegexp = /^[A-Za-z]+$/;
let numberRegexp = /^[0-9]+$/;
class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
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]: StringValidator; } = {};
validators["ZIP code"] = new ZipCodeValidator();
validators["Letters only"] = new LettersOnlyValidator();
// Show whether each string passed each validator
strings.forEach(s => {
for (let name in validators) {
console.log(""" + s + "" " + (validators[name].isAcceptable(s) ? " matches " : " does not match ") + name);
}
});
随着更多验证器的加入,我们需要一种手段来组织代码,以便于在记录它们类型的同时还不用担心与其它对象产生命名冲突。 因此,我们把验证器包裹到一个命名空间内,而不是把它们放在全局命名空间下。
下面的例子里,把所有与验证器相关的类型都放到一个叫做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
strings.forEach(s => {
for (let name in validators) {
console.log(`"${ s }" - ${ validators[name].isAcceptable(s) ? "matches" : "does not match" } ${ name }`);
}
});
当应用变得越来越大时,我们需要将代码分离到不同的文件中以便于维护。
现在,我们把Validation
命名空间分割成多个文件。 尽管是不同的文件,它们仍是同一个命名空间,并且在使用的时候就如同它们在一个文件中定义的一样。 因为不同文件之间存在依赖关系,所以我们加入了引用标签来告诉编译器文件之间的关联。 我们的测试代码保持不变。
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
}
/// <reference path="Validation.ts" />
namespace Validation {
const lettersRegexp = /^[A-Za-z]+$/;
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
}
/// <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);
}
}
}
/// <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
strings.forEach(s => {
for (let name in validators) {
console.log(""" + s + "" " + (validators[name].isAcceptable(s) ? " matches " : " does not match ") + name);
}
});
当涉及到多文件时,我们必须确保所有编译后的代码都被加载了。 我们有两种方式。
第一种方式,把所有的输入文件编译为一个输出文件,需要使用--outFile
标记:
tsc --outFile sample.js Test.ts
编译器会根据源码里的引用标签自动地对输出进行排序。你也可以单独地指定每个文件。
tsc --outFile sample.js Validation.ts LettersOnlyValidator.ts ZipCodeValidator.ts Test.ts
第二种方式,我们可以编译每一个文件(默认方式),那么每个源文件都会对应生成一个JavaScript文件。 然后,在页面上通过 <script>
标签把所有生成的JavaScript文件按正确的顺序引进来,比如:
<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()"
注意,我们并没有使用require
关键字,而是直接使用导入符号的限定名赋值。 这与使用 var
相似,但它还适用于类型和导入的具有命名空间含义的符号。 重要的是,对于值来讲, import
会生成与原始符号不同的引用,所以改变别名的var
值并不会影响原始变量的值。
为了描述不是用TypeScript编写的类库的类型,我们需要声明类库导出的API。 由于大部分程序库只提供少数的顶级对象,命名空间是用来表示它们的一个好办法。
我们称其为声明是因为它不是外部程序的具体实现。 我们通常在 .d.ts
里写这些声明。 如果你熟悉C/C++,你可以把它们当做 .h
文件。 让我们看一些例子。
流行的程序库D3在全局对象d3
里定义它的功能。 因为这个库通过一个 <script>
标签加载(不是通过模块加载器),它的声明文件使用内部模块来定义它的类型。 为了让TypeScript编译器识别它的类型,我们使用外部命名空间声明。 比如,我们可以像下面这样写:
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 let d3: D3.Base;
简介这篇指南的目的是教你如何书写高质量的TypeScript声明文件。 我们在这里会展示一些API的文档,还有它们的使用示例, 并且阐...
TypeScript是由微软开发的自由和开源的编程语言。TypeScript是JavaScript的一个超集,从今天数以百万计的JavaScript开发者所熟悉...
1. RegExp 构造函数在 ES5 中, RegExp 构造函数的参数有两种情况。第一种情况是,参数是字符串,这时第二个参数表示正则表达式...
1. 概述Reflect 对象与Proxy 对象一样,也是 ES6 为了操作对象而提供的新 API。 Reflect 对象的设计目的有这样几个。(1) 将 Ob...
1. 概述历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其...
IFrame height 属性 Frame/IFrame 对象定义和用法height 属性可设置或者返回 iframe 元素中 height 属性的值。height 属性指定了...
Submit type 属性 Submit 对象定义和用法type 属性可返回确认按钮的表单元素类型。对于确认按钮,该属性总是 "submit"。语法subm...
Script defer 属性 Script 对象实例检查当页面完成解析时是否执行脚本:var x = document.getElementById("myScript").deferx 输...