不用的 TypeScript?命名空间。

1,444 阅读3分钟

关于术语的一点说明: TypeScript 1.5 里, “内部模块”现在称做“命名空间”,“外部模块”现在则简称为“模块”。

JavaScript 中,我们需要一种手段来组织代码,以防止对象之间产生命名冲突。 因此,我们把代码包裹到一个个命名空间内,而不是把它们放在全局命名空间下,可以有效避免全局污染。

虽然在 ES6 中引入了模块系统后,不需要再考虑全局污染问题,但当我们使用全局类库时,命名空间依然是个比较好的解决方案。

使用

  • a.ts

    namespace Shape {
      const pi = Math.PI;
      export function circle(r: number) {
        return pi * r ** 2;
      }
      export interface Person {
        name: string;
        age: number;
      }
    }
    

命名空间内可以定义若干变量,这些变量只在命名空间下可见;如果想要在全局使用,可以使用 export

分离到多文件

随着程序变大,命名空间也可以进行拆分。

  • b.ts
    /// <reference path='./a.ts' />
    namespace Shape {
      export function square(w: number) {
        return w * w;
      }
    }
    

虽然在不同的文件中,但他们是同一个命名空间,使用时就如同定义在一个文件中的命名空间一样使用。 因为不同文件间存在引用关系,所以要用引用标志告诉编辑器文件之间的关联。

/// <reference path='a.ts'/>

当涉及到多文件时,我们必须确保所有编译后的代码都被加载了。

  • 方法一

    把所有的输入文件编译为一个输出文件,需要使用 --outFile 标记:

    tsc --outFile sample.js Test.ts
    

    编译器会根据源码里的引用标签自动地对输出进行排序。

  • 方法二

    每个源文件都会对应生成一个 JavaScript 文件。 然后,在页面上通过 <script> 标签把所有生成的 JavaScript 文件按正确的顺序引进来,

    <script src="Test.js" type="text/javascript" />
    <script src="a.js" type="text/javascript" />
    <script src="b.js" type="text/javascript" />
    

别名

使用 import q = x.y.z 给常用的对象起一个短的名字,可以简化命名空间操作。

namespace Shape {
  export function square(w: number) {
    return w * w;
  }
}

import square = Shape.square;
square(); // same as "Shape.square()"

不要与用来加载模块的 import x = require('name') 语法弄混了,这里的语法是为指定的符号创建一个别名。 你可以用这种方法为任意标识符创建别名,也包括导入的模块中的对象。

最后

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

模块会把依赖添加到模块加载器上(例如CommonJs / Require.js)。这会带来长久的模块化和可维护性上的便利。模块也提供了更好的代码重用,更强的封闭性以及更好的使用工具进行优化。

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

ES6 开始,模块成为了语言内置的部分,应该会被所有正常的解释引擎所支持。 因此,对于新项目来说推荐使用模块做为组织代码的方式。ts 保留命名空间,更多的考虑是对全局变量时代的兼容。

TypeScript 工程系列