在 JS 中命名空间可以有效的避免全局污染,但是 ES6 引入模块系统之后,就很少被提及了。TS 也实现了这个特性,尽管在模块系统中,我们不需要考虑全局污染问题,但是要使用一些全局的类库,命名空间依然是一种比较好的解决方案。

    命名空间使用 namespace 关键字声明:

    1. namespace Shape {
    2. let a = 1; // 只在命名空间内可用
    3. export let b = 2; // 导出到全局
    4. }

    命名空间还可以进行拆分:

    namespace Shape {
      let a = 1; // 只在命名空间内可用
    
      export let b = 2; // 导出到全局
    
      export function circle(r:number) { 
        return Math.PI * r * r;
      }
    }
    
    namespace Shape {
      export function square(x: number) {
        return x * x;
      }
    }
    
    Shape.b;
    Shape.circle(1);
    Shape.square(1);
    

    只要名称相同,就会合并到一个全局变量中。

    命名空间应该在全局环境下使用,不要在模块中使用。

    命名空间编译之后的代码是一个 IIFE

    (function (Shape) {
        var a = 1; // 只在命名空间内可用
        Shape.b = 2; // 导出到全局
        function circle(r) {
            return Math.PI * r * r;
        }
        Shape.circle = circle;
    })(Shape || (Shape = {}));
    

    当我们把命名空间拆分为两个文件时,在一个文件中使用了另一个文件中命名空间抛出的方法:

    // a.ts
    namespace Shape {
      let a = 1; // 只在命名空间内可用
    
      export let b = 2; // 导出到全局
    
      export function circle(r:number) { 
        return Math.PI * r * r;
      }
    }
    
    // b.ts
    namespace Shape {
      export function square(x: number) {
        return x * x;
      }
    }
    
    Shape.b;
    Shape.circle(1);
    Shape.square(1);
    

    编译 b.ts 时会报错,此时在 b.ts 中需要使用 /// 语法进行引用才能编译:

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

    此时编译之后,a 和 b 都会被编译出来