命名空间

关于术语的说明:需要注意的是,在 TypeScript 1.5 中,术语发生了变化。“内部模块”现在是“命名空间”。“外部模块”现在只是“模块”,目的是与 ECMAScript 2015 的术语保持一致(即 module X { 等效于现在更常用的 namespace X {)。

这篇文章概述了使用 TypeScript 中的命名空间(以前称为“内部模块”)组织代码的各种方法。正如我们在术语说明中提到的,“内部模块”现在称为“命名空间”。此外,在声明内部模块时任何使用 module 关键字的地方都可以且应该改用 namespace 关键字。这避免了通过类似命名的术语让新用户感到困惑。

第一步

让我们从我们将在本页中用作示例的程序开始。我们编写了一小套简单的字符串验证器,就像您在网页中检查用户在表单上的输入或检查外部提供的数据文件的格式时所编写的那样。

单个文件中的验证器

ts
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

命名空间验证器

ts
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
ts
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
}
LettersOnlyValidator.ts
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
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);
}
}
}
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(摘录)
html
<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") 语法混淆,此语法只是为指定的符号创建一个别名。您可以将这些类型的导入(通常称为别名)用于任何类型的标识符,包括从模块导入中创建的对象。

ts
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(简化摘录)
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;

TypeScript 文档是一个开源项目。通过 发送拉取请求 ❤ 帮助我们改进这些页面

此页面的贡献者
MHMohamed Hegazy (56)
OTOrta Therox (15)
DRDaniel Rosenwasser (3)
IOIván Ovejero (1)
JBJack Bates (1)
14+

上次更新:2024 年 3 月 21 日