在线文档教程

Namespaces

命名空间

关于术语的说明:重要的是要注意,在 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 for (let s of strings) { for (let name in validators) { let isMatch = validators[name].isAcceptable(s console.log(`'${ s }' ${ isMatch ? "matches" : "does not match" } '${ name }'.` } }

命名空间

当我们添加更多验证器时,我们希望拥有某种组织方案,以便跟踪我们的类型,而不必担心与其他对象的名称冲突。我们不必将大量不同的名称放入全局名称空间,而是将我们的对象包装到名称空间中。

在这个例子中,我们将所有与验证器相关的实体移动到名为Validation空间的名称空间中。因为我们希望这里的接口和类在命名空间外部可见,所以我们在前面加上export。相反,这些变量lettersRegexpnumberRegexp实现细节,因此它们是未提交的,并且在命名空间之外的代码将不可见。在文件底部的测试代码中,我们现在需要在命名空间外使用类型的名称,例如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标志的连接输出将所有输入文件编译为单个JavaScript输出文件:

tsc --outFile sample.js Test.ts

编译器将根据文件中存在的引用标签自动对输出文件进行排序。您也可以分别指定每个文件:

tsc --outFile sample.js Validation.ts LettersOnlyValidator.ts ZipCodeValidator.ts Test.ts

或者,我们可以使用每个文件编译(默认)为每个输入文件发出一个 JavaScript 文件。如果生成了多个 JS 文件,我们需要<script>在网页上使用标签以适当的顺序加载每个发出的文件,例如:

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" />

Aliases

您可以简化名称空间的另一种方法是使用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不会反映在原始变量中。

使用其他 JavaScript 库

为了描述不是用 TypeScript 编写的库的形状,我们需要声明库公开的 API。由于大多数 JavaScript 库仅公开一些顶级对象,因此命名空间是表示它们的好方法。

我们称这些声明没有定义“环境”实现。通常这些是在.d.ts文件中定义的。如果您熟悉 C / C ++,则可以将它们视为.h文件。我们来看几个例子。

环境命名空间

流行的库 D3 在一个全局对象中定义了它的功能d3。因为这个库是通过一个<script>标签(而不是一个模块加载器)加载的,所以它的声明使用命名空间来定义它的形状。对于 TypeScript 编译器来看这个形状,我们使用一个环境名称空间声明。例如,我们可以开始编写它,如下所示:

D3.d.ts (simplified excerpt)

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;