模块
在TypeScript中利用模块(module)来组织代码。这里将讨论内部和外部模块,以及在何时使用哪种方式更合适,以及怎么使用。当然也会讨论一些高级话题,例如如何使用外部模块,以及在TypeScript中使用模块时常见的一些错误。
第一步
让我们从下面的例子开始,这个例子将会贯穿本文。首先我们写了一段字符串验证代码,用来验证用户在web页面表单输入的信息,或者是检查外部文件提供的数据格式。
在单个文件中的Validator
interface StringValidator {
isAcceptable(s: string): boolean;
}
var lettersRegexp = /^[A-Za-z]+$/;
var numberRegexp = /^[0-9]+$/;
class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
// Some samples to try
var strings = ['Hello', '98052', '101'];
// Validators to use
var 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 (var name in validators) {
console.log('"' + s + '" ' + (validators[name].isAcceptable(s) ? ' matches ' : ' does not match ') + name);
}
});
增加模块
当增加更多的验证逻辑时,我们希望利用某种组织方式跟踪验证类型,而不必担心与其他对象的名称冲突。不是将大量的不同名称放在全局命名空间中,而是将对象封装成模块。
在下面例子中,我们将把所有与validator相关的类型都移到一个'Validation'模块中。因为我们希望接口和类对外部模块可见,所以我们在前面加上了'export'关键字导出接口和类。相反,变量lettersRegexp和numberRegexp是实现细节,我们并不希望暴露给外部模块。在文件最后面的测试代码中,要想在外部模块使用就需要用模块名称限定类型名称,例如Validation.LettersOnlyValidator。
模块化的Validators
module Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
var lettersRegexp = /^[A-Za-z]+$/;
var numberRegexp = /^[0-9]+$/;
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
}
// Some samples to try
var strings = ['Hello', '98052', '101'];
// Validators to use
var validators: { [s: string]: Validation.StringValidator; } = {};
validators['ZIP code'] = new Validation.ZipCodeValidator();
validators['Letters only'] = new Validation.LettersOnlyValidator();
// Show whether each string passed each validator
strings.forEach(s => {
for (var name in validators) {
console.log('"' + s + '" ' + (validators[name].isAcceptable(s) ? ' matches ' : ' does not match ') + name);
}
});
拆分为多个文件
随着应用程序的增长,希望将代码分为多个文件,使维护更容易。
下面,将Validation模块的内容分到多个文件中。虽然这些文件是分开的,但每一个文件都对同一个模块做出贡献,当使用时就像是在一个地方定义的。由于文件之间存在依赖关系,因此我们添加了reference标签告诉编译器这些文件之间的关系。测试代码保持不变。
由多个文件组成内部模块
Validation.ts
module Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
}
LettersOnlyValidator.ts
/// <reference path="Validation.ts" />
module Validation {
var lettersRegexp = /^[A-Za-z]+$/;
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
}
ZipCodeValidator.ts
/// <reference path="Validation.ts" />
module Validation {
var numberRegexp = /^[0-9]+$/;
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
}
Test.ts
/// <reference path="Validation.ts" />
/// <reference path="LettersOnlyValidator.ts" />
/// <reference path="ZipCodeValidator.ts" />
// Some samples to try
var strings = ['Hello', '98052', '101'];
// Validators to use
var validators: { [s: string]: Validation.StringValidator; } = {};
validators['ZIP code'] = new Validation.ZipCodeValidator();
validators['Letters only'] = new Validation.LettersOnlyValidator();
// Show whether each string passed each validator
strings.forEach(s => {
for (var name in validators) {
console.log('"' + s + '" ' + (validators[name].isAcceptable(s) ? ' matches ' : ' does not match ') + name);
}
});
一旦涉及多个文件,就需要保证编译器能加载各个文件。这里有两种方式做到:
第一种方式:可以利用 --out flag连接多个输入文件让编译器编译输出一个JavaScript文件。
tsc --out sample.js Test.ts
编译器会根据文件中的reference标签排序输出文件。
也可以指定每一个文件:
tsc --out sample.js Validation.ts LettersOnlyValidator.ts ZipCodeValidator.ts Test.ts
第二种方式:可以对每个输入文件单独编译(缺省方式)生成一个JavaScript文件。如果生成了多个JS文件,可以在web页面按照一定的顺序使用