关于术语的一点说明:TypeScript 1.5 术语名称已经发生了变化。“内部模块”现在称为“命名空间”,“外部模块” 现在则简称为 “模块”,这是为了与 ES6 的术语保持一致,也就是说 module X { 相当于现在推荐的写法 namespace X {

一、介绍

这篇文章主要介绍的是 TypeScript 里面使用 module 和 namespace 组织代码的方法,以及一些高级使用的场景或者在使用的时候的一些潜在的问题。

相关文章:

二、使用 namespace

namespace 是位于全局 namespace 下的一个普通的带有名字的 JavaScript 对象,这使得 namespace 非常容易使用。

它们可以在多文件中同时使用,并且通过 --outFile 结合在一起。

namespace 是帮你组织 Web 应用不错的方式,你可以吧所有的依赖都放在 HTML 的 <script> 标签里。

不过,就像其他的全局 namespace 污染一样,它很难去识别组件之间的依赖关系,尤其是在大型的应用中。

三、使用模块

像命名空间一样,模块可以包含代码和声明。不同的是模块可以声明它的依赖。

模块会把依赖添加到模块加载器上(比如 CommonJs / Require.js)对于小型的 JS 应用来说可能没必要,但是对于大型应用,这一点点的花费会带来长久的模块化和可维护性上的便利。

模块也提供了更好的代码重用,更强的封闭性以及更好的使用工具进行优化。

对于 Node.js 应用来说,模块是默认并且推荐的组织代码的方式。

从 ES6 开始,模块成了语言内置的部分,应该会被所有正常的解释引擎所支持,因此对于新项目来说,非常推荐使用模块作为组织代码的方式。

四、namespace 和 module 的陷阱

对 module 使用 /// <reference>

一个常见的错误时使用 /// <reference> 引用 module 文件,应该使用 import。要理解这之间的区别,首先应该弄清楚编译器是如何根据 import 路径(比如:import x from "..."; 或者 import x = require('...') 里面的 ...) 来定位模块的类型信息的。

编译器首先尝试去查找相应路径下的 .ts, .tsx, .d.ts,如果这些文件都找不到,编译器会查找 外部模块声明,而外部模块声明是在 .d.ts 声明的。

myModules.d.ts:

declare module "someModule" {
    export function fn(): string
}

myOtherModule.ts

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

上面的引用标签中指定了外来模块的位置

不必要的 namespace

如果想要把命名空间转换成 module,可能会像下面这个文件一样:

shapes.ts

export namespace Shapes {
    export class Triangle {}
    export class Square {}
}

顶层的模块 Shapes 包裹了 TriangleSquare ,对于使用它的人来说是非常恶心的:

shapeConsumer.ts

import * as shapes from './shapes';

let t = new shapes.Shapes.Triangle();

TypeScript 的 module 的一个特点是不同的 module 永远也不会在相同的作用域内使用相同的名字。因为使用 module 的人会为它们命名,所以完全没有必要把导出的符号包裹在一个 namespace 里面。

使用命名空间是为了提供逻辑分组和避免命名冲突,而模块本身已经是一个逻辑分组,并且它的名字是由导入这个模块的代码指定,因此没有必要为导出的对象增加额外的模块层。

下面是改进的例子:

shapes.ts:

export class Triangle { /* ... */ }
export class Square { /* ... */ }

shapeConsumer.ts:

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

模块的取舍

就像每个 JS 文件对应一个模块一样,TypeScript 里面模块文件与生成的 JS 也是一一对应的,这会产生一种影响,根据你指定的目标模块系统的不同,你可能无法连接多个模块源文件。

比如当目标模块系统是 CommonJS 或者 UMD时,无法使用 outFile 选项,但是当目标模块系统为 amd 或者 system时, TypeScript 1.8 以上版本能够使用 outFile