关于名称的说明:TypeScript 1.5 中发生了变化,“内部模块” 现在成为 “命名空间”“外部空间” 现在简称为 “模块”,与 ES6 保持一致,因此 module X{} 现在的推荐写法 namespace X{

一、说明

ES6 引入的 module 的概念,而 TypeScript 则是沿用这个概念。

模块在其自身的作用域里面执行,而不是在全局作用域,这意味着定义在一个模块里的变量,函数,类等等在模块外部是不可见的,除非明确的通过 export 将其导出。相反,如果想使用其他模块导出的变量、函数、类或者接口,必须导入,比如 import 的形式。

模块是自声明的,两个模块之间的关系是通过在文件级别上使用 importexport 简历的。

模块使用模块加载器去导入其他的模块,在运行的时候,模块加载器的作用是在执行此模块代码前去查找并执行这个模块的所有以来。

最熟知的 JavaScript 模块加载器是服务于 Node.js CommonJS 和 服务于 Web 应用的 Require.js

TypeScript 和 ES6 一样,任何包含顶级 import 或者是 export 的文件都被当成一个模块。相反的,如果一个文件不带有顶级的 import 或者是 export 声明,那么它的内容被视为全局可见的。(因此对模块也是可见的)

二、导出

导出声明

任何声明(比如变量、函数、类、类型别名或者是接口)都能够通过添加 export 关键字来导出。

Validation.ts

export interface StringValidator {
    isAcceptable(s: string): boolean;
}

ZipCodeValidator.ts:

export const numberRegexp = /^[0-9]+$/;

export class ZipCodeValidator implements StringValidator {
    idAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}

导出语句

导出语句非常便利,因为可能需要对导出的部分重命名,所以可以改写名字:

ZipCodeValidator.ts

class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}
export { ZipCodeValidator };
export { ZipCodeValidator as mainValidator };

通过 export { a as b } 可以重命名导出的名字

重新导出

有时候可能会去扩展其他的模块,并且只导出那个模块的部分内容。重新导出功能并不会在当前模块导入那个模块或者定义一个新的局部变量。

ParseIntBasedZipCodeValidator.ts

export class ParseIntBasedZipCodeValidator {
    isAcceptable(s: string) {
        return s.length === 5 && parseInt(s).toString() === s;
    }
}

// 导出之前写的的 zipCodeValidator 验证器但是做了重命名
export {zipCodeValidator as RegExpBasedZipCodeValidator} from './ZipCodeValidator';

或者一个模块可以包裹多个模块,并把他们导出的内容联合在一起通过语法: export * from "module"

AllValidators.ts

export * from './StringValidator';
export * from './LetterOnlyValidator';
export * from './ZipCodeVlidator';

三、导入

import 和 export 操作一样简单,使用 import 的形式如下:

1、导入一个模块中某个导出的内容

import {ZipCodeValidator} from './ZipCodeValidator';

let myValidator = new ZipCodeValidator();

导入的时候还可以重命名:

import { ZipCodeValidator as ZCV } from './ZipCodeValidator';

let myValudator = new ZCV();

2、将整个模块导入到一个变量,并通过这个对象访问导入的模块的其他内容:

import * as Validator from './ZipCodeValidator';

let myValidator = new Validator.ZipCodeValidator();

3、具有副作用的导入模块

不推荐副作用的模块导入导出

一些模块会设置某些全局变量供其他的模块使用,这些模块可能没有任何的导出或用户根本不关注其导出.

import './xxx-module.js';

四、默认导出

每个模块都可以有一个 default 导出。默认导出使用 default 关键字标记了并且一个模块只能拥有一个 default 导出,需要使用一种特殊的导入形式来导入 default 导出。

default 导出非常的便利,像是 JQuery 这样的类库可以只导出一个 JQquery 或者 $,而且经常会使用同样的名字导出 JQuery。

JQuery.d.ts

declare let $: JQuery

App.ts:

import $ from 'JQuery';
$("button.continue").html("Next Step...")

类和函数生命可以直接被标记为默认导出。标记为默认导出的类和函数的名字是可以省略的。

ZipCodeValidator.ts:

export default class ZipCodeValidator {
    static numberRegexp = /^[0-9]+$/;
    isAcceptable(s: string) {
        return s.length === 5 && ZipCodeValidator.numberRegexp.test(s);
    }
}

Test.ts:

import validator from './ZipCodeValidator';

let myValidator = new validator();

或者

StaticZipCodeValidator.ts:

const numberRegexp = /^[0-9]+$/;
export default function (s: string) {
    return s.length === 5 && numberRegexp.test(s);
}

Test.ts:

import validate from './StaticZipCodeValidator';

let strings = ['hello', '11111', '101'];

strings.forEach(s => {
    console.log(`"${s}" ${validate(s) ? " matches" : " does not match"}`);
)};

default 导出也可以是一个值

OneTwoThree.ts:

export default '123';

Log.ts:

import num from './OneTwoThree';

console.log(num); // 123

五、export =import = require()

CommonJS 和 AMD 的环境中都有一个 exports 变量,这个变量包含了一个模块的所有导出内容。

CommonJS 和 AMD 的 exports 都可以被赋值为一个 对象, 这种情况吓其作用类似于 es6 语法里的默认导出,即 export default 语法。虽然作用相似,但是 export default 语法并不能兼容 CommonJS 和 AMD 的 exports

为了支持 CommonJS 和 AMD 的 exports ,TypeScript 提供了 export = 的语法。

export = 语法定义一个模块的导出 对象,这里的 对象 指的是 classinterfacenamespacefunction 或者是 enum

如果使用 export = 导出一个模块,则必须使用 TypeScript 的特定语法 import module = require('module') 来导入这个模块。

ZipCodeValidator.ts

 let numberRegexp = /^[0-9]+$/;

class ZipCodeValidator {
    isAcceptable (s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}
export = ZipCodeValidator;

Test.ts:

import zip = require('./ZipCodeVlidator');

let strings = ['Hello', '1111111', '101'];

let validator = new zip();

strings.forEach(a => {
     console.log(`"${ s }" - ${ validator.isAcceptable(s) ? "matches" : "does not match" }`);

});

六、生成模块代码

根据编译时指定的模块目标参数,编译器会生成相应的供 Node.jsRequire.js UMDSystemJS 或者 ES6 模块加载系统试用的代码。

如果要了解生成代码中的 definerequireregister 的意义,需要参考不同模块加载器的文档。

下面的代码 TypeScript 会将其编译成如图所示:(忽略 mod 不存在错误)默认走 AMD / RequireJS

import m = require("mod");
export let t = m.something + 1;

1.jpg

AMD/RequireJS 编译结果

define(["require", "exports", "./mod"], function (require, exports, mod_1) {
    exports.t = mod_1.something + 1;
});

CommonJS/Node 编译结果

let mod_1 = require("./mod");
exports.t = mod_1.something + 1;

UMD 编译结果

(function (factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        let v = factory(require, exports); if (v !== undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define(["require", "exports", "./mod"], factory);
    }
})(function (require, exports) {
    let mod_1 = require("./mod");
    exports.t = mod_1.something + 1;
});

SystemJS 编译结果

System.register(["./mod"], function(exports_1) {
    let mod_1;
    let t;
    return {
        setters:[
            function (mod_1_1) {
                mod_1 = mod_1_1;
            }],
        execute: function() {
            exports_1("t", t = mod_1.something + 1);
        }
    }
});

ES6 编译结果

import { something } from "./mod";
export let t = something + 1;

七、简单示例

整理一下前面代码的验证期实现,每个模块只有一个命名的导出

编译的时候,可以在命令行上制定一个模块目标,对于 Node.js 来说使用 --module commonjs 而对于 Require.js 来说,使用 --module amd:

tsc --module commonjs Test.ts

编译完成之后,每个模块会生成一个单独的 js 文件:

Validation.ts:

export interface StringValidator {
    isAcceptable(s: string): boolean;
}

LettersOnlyValidator.ts:

import { StringValidator } from './Validation';

const lettersRegexp = /^[A-Za-z]+$/;

export class LettersOnlyWithValidator implements StringValidator {
    isAcceptable(s: string) {
        return letterRegexp.test(s);
    }
}

ZipCodeValidator.ts

import { StringValidator } from "./Validation";

const numberRegexp = /^[0-9]+$/;

export class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}

Test.ts:

import { StringValidator } from "./Validation";
import { ZipCodeValidator } from "./ZipCodeValidator";
import { LettersOnlyValidator } from "./LettersOnlyValidator";

// Some samples to try
let strings = ["Hello", "98052", "101"];

// Validators to use
let validators: { [s: string]: StringValidator; } = {};
validators["ZIP code"] = new ZipCodeValidator();
validators["Letters only"] = new LettersOnlyValidator();

// Show whether each string passed each validator
strings.forEach(s => {
    for (let name in validators) {
        console.log(`"${ s }" - ${ validators[name].isAcceptable(s) ? "matches" : "does not match" } ${ name }`);
    }
});

编译结果:

2.jpg