TypeScript 模塊
在沒有使用模塊化編程的時代,會經(jīng)常遇到全局變量污染、變量重名、多個文件之間存在依賴關(guān)系,需要保證一定加載順序等問題。在模塊化這種規(guī)范被提出后,得到社區(qū)和廣大開發(fā)者的積極響應(yīng)。
本節(jié)將介紹 TypeScript 的模塊化方案,學(xué)習(xí)模塊的導(dǎo)入導(dǎo)出機制,要注意 TypeScript 是怎么樣兼容 CommonJS 和 AMD 規(guī)范的。
1. 慕課解釋
模塊在其自身的作用域里執(zhí)行,而不是在全局作用域里。
export
: 導(dǎo)出模塊中的變量、函數(shù)、類、接口等;
import
: 導(dǎo)入其他模塊導(dǎo)出的變量、函數(shù)、類、接口等。
TypeScript 與 ECMAScript 2015 一樣,任何包含頂級 import
或者 export
的文件都被當(dāng)成一個模塊。相反的,如果一個文件不帶有頂級的 import
或者 export
聲明,那么它的內(nèi)容被視為全局可見的。
2. 全局模塊
在一個 TypeScript 工程創(chuàng)建一個 test.ts
文件,寫入代碼:
const a = 1
然后,在相同的工程下創(chuàng)建另一個 test2.ts
文件,寫入代碼:
const a = 2
此時編譯器會提示重復(fù)定義錯誤,雖然是在不同的文件下,但處于同一全局空間。
如果加上 export
導(dǎo)出語句:
export const a = 1
這樣,兩個 a
因處于不同的命名空間,就不會報錯。
3. 導(dǎo)出語法
3.1 使用 export 導(dǎo)出聲明
任何聲明(比如變量,函數(shù),類,類型別名或接口)都能夠通過添加 export
關(guān)鍵字來導(dǎo)出。
export.ts
:
export const a: number = 1
export const add = (x: number, y:number) => x + y
export interface User {
nickname: string,
department: string
}
export class Employee implements User {
public nickname!: string
public department!: string
}
export type used = true | false
解釋: 每個聲明都通過 export
關(guān)鍵字導(dǎo)出。
3.2 先聲明,后導(dǎo)出
const a: number = 1
const add = (x: number, y:number) => x + y
interface User {
nickname: string,
department: string
}
class Employee implements User {
public nickname!: string
public department!: string
}
type used = true | false
export { a, add, Employee }
解釋: 先進(jìn)行聲明操作,最終統(tǒng)一使用 export
關(guān)鍵字導(dǎo)出。
3.3 導(dǎo)出時重命名
const a: number = 1
const add = (x: number, y:number) => x + y
interface User {
nickname: string,
department: string
}
class Employee implements User {
public nickname!: string
public department!: string
}
type used = true | false
export { add }
export { a as level, used as status, Employee }
解釋: 在導(dǎo)出時,可以用 as
關(guān)鍵字將聲明重命名。
3.4 重新導(dǎo)出
重新導(dǎo)出功能并不會在當(dāng)前模塊導(dǎo)入那個模塊或定義一個新的局部變量。
ZipCodeValidator.ts
:
export interface StringValidator {
isAcceptable(s: string): boolean
}
export const numberRegexp = /^[0-9]+$/
class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s)
}
}
export { ZipCodeValidator }
export { ZipCodeValidator as mainValidator }
ParseIntBasedZipCodeValidator.ts
:
export class ParseIntBasedZipCodeValidator {
isAcceptable(s: string) {
return s.length === 5 && parseInt(s).toString() === s
}
}
// 導(dǎo)出原先的驗證器但做了重命名
export {ZipCodeValidator as RegExpBasedZipCodeValidator} from './ZipCodeValidator'
代碼解釋: 在 ParseIntBasedZipCodeValidator.ts 文件中,重新導(dǎo)出 ZipCodeValidator.ts 文件中的聲明。
或者一個模塊可以包裹多個模塊,并把他們導(dǎo)出的內(nèi)容聯(lián)合在一起通過語法:export * from 'module'
。
比如在 validator.ts
文件中,統(tǒng)一導(dǎo)出這兩個模塊。
// validator.ts
export * from './ZipCodeValidator'
export * from './ParseIntBasedZipCodeValidator'
3.5 默認(rèn)導(dǎo)出
export default class ZipCodeValidator {
static numberRegexp = /^[0-9]+$/
isAcceptable(s: string) {
return s.length === 5 && ZipCodeValidator.numberRegexp.test(s)
}
}
代碼解釋: 每個模塊都可以有一個 default
導(dǎo)出,且一個模塊只能夠有一個 default
導(dǎo)出。
4. 導(dǎo)入語法
4.1 使用 import 導(dǎo)入
使用 import
形式來導(dǎo)入其它模塊中的導(dǎo)出內(nèi)容。
import { a, add, Employee } from './export'
4.2 導(dǎo)入時重命名
import { a as level, used as status } from './export'
4.3 將整個模塊導(dǎo)入到一個變量
將整個模塊導(dǎo)入到一個變量,并通過它來訪問模塊的導(dǎo)出部分
import * as TYPES from './export'
4.4 直接導(dǎo)入
import './export'
5. export =
和 import = require()
CommonJS 和 AMD 的環(huán)境里都有一個 exports
變量,這個變量包含了一個模塊的所有導(dǎo)出內(nèi)容。
CommonJS 和 AMD 的 exports
都可以被賦值為一個 對象
, 這種情況下其作用就類似于 EcmaScript 2015 語法里的默認(rèn)導(dǎo)出,即 export default
語法了。雖然作用相似,但是 export default
語法并不能兼容 CommonJS 和 AMD 的 exports
。
為了支持 CommonJS 和 AMD 的 exports, TypeScript 提供了 export =
語法。
export =
語法定義一個模塊的導(dǎo)出 對象
。 這里的 對象
一詞指的是類,接口,命名空間,函數(shù)或枚舉。
若使用 export =
導(dǎo)出一個模塊,則必須使用 TypeScript 的特定語法 import module = require('module')
來導(dǎo)入此模塊。
export =
只能導(dǎo)出對象
export =
導(dǎo)出的模塊只能用import = require()
形式導(dǎo)入
文件 ZipCodeValidator.ts
:
let numberRegexp = /^[0-9]+$/
class ZipCodeValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s)
}
}
export = ZipCodeValidator
代碼解釋: 使用 export = 語法導(dǎo)出一個類對象。
文件 Test.ts
:
import Zip = require('./ZipCodeValidator')
// Some samples to try
let strings = ['Hello', '98052', '101']
// Validators to use
let validator = new Zip()
// Show whether each string passed each validator
strings.forEach(s => {
console.log(`'${ s }' - ${ validator.isAcceptable(s) ? 'matches' : 'does not match' }`)
});
代碼解釋: 通過 import = require()
形式導(dǎo)入。
6. 小結(jié)
可以看到 TypeScript 的模塊機制基本采用的是 ES6 的內(nèi)置模塊化機制,另外添加了 export =
形式來兼容 AMD 與 CommonJS 規(guī)范。