命名空间和模块

本文概述了在 TypeScript 中使用模块(Modules)和命名空间(Namespaces)组织代码的各种方式。我们还将探讨有关如何使用命名空间和模块的一些进阶主题,并解决在 TypeScript 中使用它们时常见的一些缺陷。

有关 ES 模块的更多信息,请参阅模块(Modules)文档。有关 TypeScript 命名空间的更多信息,请参阅命名空间(Namespaces)文档。

注意:在非常早期的 TypeScript 版本中,命名空间被称为“内部模块”(Internal Modules),它们出现的时间早于 JavaScript 的模块系统。

使用模块

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

模块还依赖于模块加载器(如 CommonJs/Require.js)或支持 ES 模块的运行时。模块提供了更好的代码复用性、更强的隔离性以及更好的打包工具支持。

值得注意的是,对于 Node.js 应用程序,模块是默认选项,并且我们建议在现代代码中使用模块而非命名空间

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

使用命名空间

命名空间是 TypeScript 特有的一种组织代码的方式。
命名空间本质上只是全局命名空间中具名的 JavaScript 对象。这使得命名空间成为一种非常易于使用的结构。与模块不同,它们可以跨越多个文件,并可以使用 outFile 进行合并。对于 Web 应用程序,如果所有依赖项都以 <script> 标签的形式包含在 HTML 页面中,命名空间是一种组织代码的好方法。

和所有全局命名空间污染一样,在大型应用程序中识别组件依赖关系可能会变得困难。

命名空间与模块的常见缺陷

在本节中,我们将描述使用命名空间和模块时常见的各种缺陷,以及如何避免它们。

对模块使用 /// <reference>

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

编译器会尝试按照路径顺序查找 .ts.tsx,然后是 .d.ts 文件。如果找不到特定文件,编译器会查找环境模块声明(ambient module declaration)。请记住,这些声明需要在 .d.ts 文件中定义。

  • myModules.d.ts

    ts
    // In a .d.ts file or .ts file that is not a module:
    declare module "SomeModule" {
    export function fn(): string;
    }
  • myOtherModule.ts

    ts
    /// <reference path="myModules.d.ts" />
    import * as m from "SomeModule";

这里的引用标签允许我们定位包含环境模块声明的声明文件。这就是许多 TypeScript 示例所使用的 node.d.ts 文件的引用方式。

不必要的命名空间

如果您正在将程序从命名空间转换为模块,很容易得到类似这样的文件:

  • shapes.ts

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

这里的顶层命名空间 ShapesTriangleSquare 包裹起来,这是没有必要的。这对模块的使用者来说既令人困惑又很麻烦。

  • shapeConsumer.ts

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

TypeScript 模块的一个关键特性是:两个不同的模块绝不会将名称贡献给同一个作用域。因为模块的使用者决定了给它分配什么名称,所以没有必要主动地将导出的符号包裹在命名空间中。

再次强调为什么不应该尝试对模块内容进行命名空间处理:命名空间的一般目的是提供结构的逻辑分组并防止命名冲突。因为模块文件本身已经是一个逻辑分组,并且其顶层名称由导入它的代码定义,因此无需为导出的对象额外添加一个模块层。

以下是修改后的示例

  • shapes.ts

    ts
    export class Triangle {
    /* ... */
    }
    export class Square {
    /* ... */
    }
  • shapeConsumer.ts

    ts
    import * as shapes from "./shapes";
    let t = new shapes.Triangle();

模块的权衡

正如 JS 文件与模块之间存在一一对应关系一样,TypeScript 在模块源文件与其编译后的 JS 文件之间也存在一一对应关系。这意味着根据您所使用的模块系统,无法将多个模块源文件合并在一起。例如,在以 commonjsumd 为目标时,不能使用 outFile 选项;但从 TypeScript 1.8 开始,在以 amdsystem 为目标时,可以使用 outFile

TypeScript 文档是一个开源项目。请提交 Pull Request 帮助我们改进这些页面 ❤

此页面的贡献者
DRDaniel Rosenwasser (63)
OTOrta Therox (19)
MHMohamed Hegazy (19)
BBohdan (2)
MFMartin Fischer (1)
15+

最后更新:2026 年 3 月 27 日