Skip to content
0

TypeScript Office - 命名空间与模块

命名空间与模块

这篇文章概述了在 TypeScript 中使用模块和命名空间来组织你的代码的各种方法。我们还将讨论一些关于如何使用命名空间和模块的高级话题,并解决在 TypeScript 中使用它们时的一些常见陷阱。

关于 ES 模块的更多信息,请参见 Modules 文档。更多关于 TypeScript 命名空间的信息,请参见 Namespaces 文档

注意:在非常老的 TypeScript 版本中,命名空间被称为「内部模块」,这比 JavaScript 模块系统要早。

使用模块

模块可以包含代码和声明。

模块也依赖于模块加载器(如 CommonJs/Require.js)或支持 ES 模块的运行时间。模块提供了更好的代码重用,更强的隔离性和更好的捆绑工具支持。

同样值得注意的是,对于 Node.js 应用程序,模块是默认的,我们在现代代码中推荐模块而不是命名空间。

从 ECMAScript 2015 开始,模块是语言的原生部分,所有兼容的引擎实现都应该支持。因此,对于新项目,模块将是推荐的代码组织机制。

使用命名空间

命名空间是一种 TypeScript 特有的组织代码的方式。

命名空间是全局命名空间中简单命名的 JavaScript 对象。这使得命名空间的使用非常简单。与模块不同,它们可以跨越多个文件,并且可以使用 outFile 串联。命名空间可以成为 Web 应用程序中结构化代码的一个好方法,所有的依赖关系都包含在 HTML 页面的

就像所有的全局命名空间污染一样,可能很难识别组件的依赖关系,特别是在一个大型应用程序中。

命名空间和模块的陷阱

在本节中,我们将介绍使用命名空间和模块的各种常见陷阱,以及如何避免这些陷阱。

/// <reference> 为模块命名

一个常见的错误是试图使用 /// 语法来引用一个模块文件,而不是使用 import 语句。为了理解这种区别,我们首先需要理解编译器是如何根据 import 的路径(例如,在 import x from "..."; 中的 ...import x = require("..."); 等等)路径来定位模块的类型信息。

编译器将尝试找到一个 .ts.tsx,然后是一个具有适当路径的 .d.ts。如果找不到一个特定的文件,那么编译器将寻找一个环境模块声明。回顾一下,这些需要在 .d.ts 文件中声明。

  • myModules.d.ts
typescript
// 在一个.d.ts文件或不是模块的.ts文件中
declare module "SomeModule" {
  export function fn(): string;
}
  • myOtherModule.ts
typescript
/// <reference path="myModules.d.ts" />
import * as m from "SomeModule";

这里的引用标签允许我们找到包含环境模块声明的声明文件。几个 TypeScript 样本使用的 node.d.ts 文件就是这样被消耗的。

不必要的命名方式

如果你要把一个程序从命名空间转换为模块,很容易就会出现一个看起来像这样的文件:

  • shapes.ts
typescript
export namespace Shapes {
  export class Triangle {
    /* ... */
  }
  export class Square {
    /* ... */
  }
}

这里的顶层命名空间 Shapes 毫无理由地将 Triangle 和 Square 包裹起来。这让你的模块的使用者感到困惑和厌烦。

  • shapeConsumer.ts
typescript
import * as shapes from "./shapes";
let t = new shapes.Shapes.Triangle(); // shapes.Shapes?

TypeScript 中模块的一个关键特征是,两个不同的模块永远不会将名字贡献给同一个范围。因为模块的消费者决定给它分配什么名字,所以不需要主动将导出的符号包裹在一个命名空间中。

重申一下为什么你不应该尝试对模块内容进行命名空间,命名空间的一般想法是提供结构体的逻辑分组,并防止名称碰撞。因为模块文件本身已经是一个逻辑分组,它的顶层名称由导入它的代码定义,所以没有必要为导出的对象使用一个额外的模块层。

下面是一个修改后的例子:

  • shapes.ts
typescript
export class Triangle {
  /* ... */
}
export class Square {
  /* ... */
}
  • shapeConsumer.ts
typescript
import * as shapes from "./shapes";
let t = new shapes.Triangle();

模块的权衡

就像 JS 文件和模块之间有一对一的对应关系一样,TypeScript 在模块源文件和其发射的 JS 文件之间有一对一的对应关系。这样做的一个影响是,根据你的目标模块系统,不可能串联多个模块源文件。例如,你不能在针对 commonjs 或 umd 时使用 outFile 选项,但在 TypeScript 1.8 及更高版本中,在针对 amd 或 system 时可以使用 outFile 。

最近更新