Tiven Wang
Wang Tiven February 27, 2018
425 favorite favorites
bookmark bookmark
share share

A note about terminology: It’s important to note that in TypeScript 1.5, the nomenclature has changed. “Internal modules” are now “namespaces”. “External modules” are now simply “modules”, as to align with ECMAScript 2015’s terminology, (namely that module X { is equivalent to the now-preferred namespace X {).

Introduction

Modules are declarative; the relationships between modules are specified in terms of imports and exports at the file level.

Modules 本身在 TypeScript 语言中相对简单,但是为了适应转换成不同目标语言的复杂场景, 则会出现多种表达方式, 需要注意。

TypeScript 使用了与 ECMAScript 2015 modules 相同的概念。一个 module 是一个执行 scope,在一个 module 内声明的 variables, functions, classes 等不会被外部访问到,除非 module 用 export 形式显式地暴露出他们。相对应的,要使用别的 module 暴露出来的类型则可以使用 import 形式引入他们。

当一个源码文件中顶层级别包含 import 或 export 时被认为是一个 module,一个源代码文件只对应一个 module。

module loader 是运行时负责为 module 查找定位和执行其所有依赖的引擎。JavaScript 常见的 module loader 有 Node.js 的 CommonJS module loader 和 Web applications 常使用的 require.js

Basic

Export

任何声明 declaration (例如 a variable, function, class, type alias, or interface)都可以在其前面添加关键字 export 暴露出来。

Validation.ts

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

ZipCodeValidator.ts

import { StringValidator } from "./Validation";

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

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

index.ts

import { ZipCodeValidator, numberRegexp } from "./src/ZipCodeValidator";

let myValidator = new ZipCodeValidator();
let s = "123456";

console.log(myValidator.isAcceptable(s));
console.log(myValidator.isAcceptable(s) === (s.length === 5 && numberRegexp.test(s)));

// Output:
/**
false
true
*/

Export statements

还可以使用 export 独立语句暴露声明或者暴露出重命名后的声明:

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

Re-exports

一个 module 中还可以对另一个 module 里的声明在不引入本地的情况下直接转发或重命名后暴露出去:

ParseIntBasedZipCodeValidator.ts

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

// Export original validator but rename it
export {ZipCodeValidator as RegExpBasedZipCodeValidator} from "./ZipCodeValidator";

所以可以有一个专门的 module 负责暴露所有其他 modules 的声明,使用语句export * from "module"转发所有的

AllValidators.ts

export * from "./StringValidator"; // exports interface 'StringValidator'
export * from "./LettersOnlyValidator"; // exports class 'LettersOnlyValidator'
export * from "./ZipCodeValidator";  // exports class 'ZipCodeValidator'

Import

相对应地,Import 就好理解了,引入 module 的其中一个声明

import { ZipCodeValidator } from "./ZipCodeValidator";

let myValidator = new ZipCodeValidator();

引入重命名的声明

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

let myValidator = new ZCV();

引入所有声明成为一个对象

import * as validator from "./ZipCodeValidator";

let myValidator = new validator.ZipCodeValidator();

Compatible with

Default exports

每个 module 都可以设置一个默认输出,export default 。使用 import name from module引入默认声明,那么可以任意。例如像 jQuery 这样的库可能会有默认输出 $ 或 jQuery,就可以使用下面形式引入

JQuery.d.ts

declare let $: JQuery;
export default $;

App.ts

import $ from "JQuery";

$("button.continue").html( "Next Step..." );

export = and import = require()

CommonJS 和 AMD 都有 exports 对象的概念,它包含了一个 module 的所有输出,他们也都支持把 exports 对象替换成自定义对象。export default的行为和这差不多,但两者并不兼容。所以 TypeScript 使用 export = 来表达传统的 CommonJS and AMD 工作流程。

使用 export = 语法暴露一个 module 默认的声明,使用 import module = require("module") 引入一个 module 默认的声明。对于想 jQuery 这样的库可以写成如下形式:

JQuery.d.ts

declare let $: JQuery;
export = $;

App.ts

import $ = require("JQuery");

$("button.continue").html( "Next Step..." );

Code Generation for Modules

TypeScript 可以被编译成不同的目标语言如 Node.js (CommonJS), require.js (AMD), UMD, SystemJS, or ECMAScript 2015 native modules (ES6) 的 module-loading systems 的代码。在编译工具上指定参数 --module 来指定目标语言,如 Node.js, 用 --module commonjs 。 require.js, 用 --module amd

tsc --module commonjs -p ./

设置不同的参数来查看编译后的目标语言版本差别。

关于 Modules 更多高级情况请参考 TypeScript 官方 Handbook [1]

Namespaces

namespace 在之前叫做 internal module ,可见他和 module 意思差不多。namespace 就如同 package 一样用于区分名字相同的不同开发对象,但鉴于可以通过不同的 module 文件和 import 重命名开发对象来做到区分,而且 namespace 也没有对应的 import 引入语法(只能通过 /// <reference path=”myModules.d.ts” /> 这样间接的方式)来表达依赖关系,所以 namespace 应用场景并不多。

关于 Namespaces 更多高级情况请参考 TypeScript 官方 Handbook [2]

本文相关完整代码可以下载自 Github

References

Similar Posts

Comments

Back to Top