TypeScript——命名空間

這篇文章描述了如何在TypeScript里使用命名空間(之前叫做“內(nèi)部模塊”)來(lái)組織你的代碼。 就像我們?cè)谛g(shù)語(yǔ)說(shuō)明里提到的那樣,“內(nèi)部模塊”現(xiàn)在叫做“命名空間”。 另外,任何使用 module關(guān)鍵字來(lái)聲明一個(gè)內(nèi)部模塊的地方都應(yīng)該使用namespace關(guān)鍵字來(lái)替換。 這就避免了讓新的使用者被相似的名稱所迷惑。

第一步

我們先來(lái)寫(xiě)一段程序并將在整篇文章中都使用這個(gè)例子。 我們定義幾個(gè)簡(jiǎn)單的字符串驗(yàn)證器,假設(shè)你會(huì)使用它們來(lái)驗(yàn)證表單里的用戶輸入或驗(yàn)證外部數(shù)據(jù)。

所有的驗(yàn)證器都放在一個(gè)文件里

interface StringValidator {

? ? isAcceptable(s: string): boolean;

}

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

let 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

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

for (let s of strings) {

? ? for (let name in validators) {

? ? ? ? let isMatch = validators[name].isAcceptable(s);

? ? ? ? console.log(`'${ s }' ${ isMatch ? "matches" : "does not match" } '${ name }'.`);

? ? }

}

命名空間

隨著更多驗(yàn)證器的加入,我們需要一種手段來(lái)組織代碼,以便于在記錄它們類型的同時(shí)還不用擔(dān)心與其它對(duì)象產(chǎn)生命名沖突。 因此,我們把驗(yàn)證器包裹到一個(gè)命名空間內(nèi),而不是把它們放在全局命名空間下。

下面的例子里,把所有與驗(yàn)證器相關(guān)的類型都放到一個(gè)叫做Validation的命名空間里。 因?yàn)槲覀兿胱屵@些接口和類在命名空間之外也是可訪問(wèn)的,所以需要使用 export。 相反的,變量 lettersRegexp和numberRegexp是實(shí)現(xiàn)的細(xì)節(jié),不需要導(dǎo)出,因此它們?cè)诿臻g外是不能訪問(wèn)的。 在文件末尾的測(cè)試代碼里,由于是在命名空間之外訪問(wèn),因此需要限定類型的名稱,比如 Validation.LettersOnlyValidator。

使用命名空間的驗(yàn)證器

namespace Validation {

? ? export interface StringValidator {

? ? ? ? isAcceptable(s: string): boolean;

? ? }

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

? ? const 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

let strings = ["Hello", "98052", "101"];

// Validators to use

let validators: { [s: string]: Validation.StringValidator; } = {};

validators["ZIP code"] = new Validation.ZipCodeValidator();

validators["Letters only"] = new Validation.LettersOnlyValidator();

// Show whether each string passed each validator

for (let s of strings) {

? ? for (let name in validators) {

? ? ? ? console.log(`"${ s }" - ${ validators[name].isAcceptable(s) ? "matches" : "does not match" } ${ name }`);

? ? }

}

分離到多文件

當(dāng)應(yīng)用變得越來(lái)越大時(shí),我們需要將代碼分離到不同的文件中以便于維護(hù)。

多文件中的命名空間

現(xiàn)在,我們把Validation命名空間分割成多個(gè)文件。 盡管是不同的文件,它們?nèi)允峭粋€(gè)命名空間,并且在使用的時(shí)候就如同它們?cè)谝粋€(gè)文件中定義的一樣。 因?yàn)椴煌募g存在依賴關(guān)系,所以我們加入了引用標(biāo)簽來(lái)告訴編譯器文件之間的關(guān)聯(lián)。 我們的測(cè)試代碼保持不變。

Validation.ts

namespace Validation {

? ? export interface StringValidator {

? ? ? ? isAcceptable(s: string): boolean;

? ? }

}

LettersOnlyValidator.ts

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

namespace Validation {

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

? ? export class LettersOnlyValidator implements StringValidator {

? ? ? ? isAcceptable(s: string) {

? ? ? ? ? ? return lettersRegexp.test(s);

? ? ? ? }

? ? }

}

ZipCodeValidator.ts

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

namespace Validation {

? ? const 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

let strings = ["Hello", "98052", "101"];

// Validators to use

let validators: { [s: string]: Validation.StringValidator; } = {};

validators["ZIP code"] = new Validation.ZipCodeValidator();

validators["Letters only"] = new Validation.LettersOnlyValidator();

// Show whether each string passed each validator

for (let s of strings) {

? ? for (let name in validators) {

? ? ? ? console.log(`"${ s }" - ${ validators[name].isAcceptable(s) ? "matches" : "does not match" } ${ name }`);

? ? }

}

當(dāng)涉及到多文件時(shí),我們必須確保所有編譯后的代碼都被加載了。 我們有兩種方式。

第一種方式,把所有的輸入文件編譯為一個(gè)輸出文件,需要使用--outFile標(biāo)記:

tsc --outFile sample.js Test.ts

編譯器會(huì)根據(jù)源碼里的引用標(biāo)簽自動(dòng)地對(duì)輸出進(jìn)行排序。你也可以單獨(dú)地指定每個(gè)文件。

tsc --outFile sample.js Validation.ts LettersOnlyValidator.ts ZipCodeValidator.ts Test.ts

第二種方式,我們可以編譯每一個(gè)文件(默認(rèn)方式),那么每個(gè)源文件都會(huì)對(duì)應(yīng)生成一個(gè)JavaScript文件。 然后,在頁(yè)面上通過(guò) <script>標(biāo)簽把所有生成的JavaScript文件按正確的順序引進(jìn)來(lái),比如:

MyTestPage.html (excerpt)

? ? <script src="Validation.js" type="text/javascript" />

? ? <script src="LettersOnlyValidator.js" type="text/javascript" />

? ? <script src="ZipCodeValidator.js" type="text/javascript" />

? ? <script src="Test.js" type="text/javascript" />

別名

另一種簡(jiǎn)化命名空間操作的方法是使用import q = x.y.z給常用的對(duì)象起一個(gè)短的名字。 不要與用來(lái)加載模塊的 import x = require('name')語(yǔ)法弄混了,這里的語(yǔ)法是為指定的符號(hào)創(chuàng)建一個(gè)別名。 你可以用這種方法為任意標(biāo)識(shí)符創(chuàng)建別名,也包括導(dǎo)入的模塊中的對(duì)象。

namespace Shapes {

? ? export namespace Polygons {

? ? ? ? export class Triangle { }

? ? ? ? export class Square { }

? ? }

}

import polygons = Shapes.Polygons;

let sq = new polygons.Square(); // Same as "new Shapes.Polygons.Square()"

注意,我們并沒(méi)有使用require關(guān)鍵字,而是直接使用導(dǎo)入符號(hào)的限定名賦值。 這與使用 var相似,但它還適用于類型和導(dǎo)入的具有命名空間含義的符號(hào)。 重要的是,對(duì)于值來(lái)講, import會(huì)生成與原始符號(hào)不同的引用,所以改變別名的var值并不會(huì)影響原始變量的值。

使用其它的JavaScript庫(kù)

為了描述不是用TypeScript編寫(xiě)的類庫(kù)的類型,我們需要聲明類庫(kù)導(dǎo)出的API。 由于大部分程序庫(kù)只提供少數(shù)的頂級(jí)對(duì)象,命名空間是用來(lái)表示它們的一個(gè)好辦法。

我們稱其為聲明是因?yàn)樗皇峭獠砍绦虻木唧w實(shí)現(xiàn)。 我們通常在 .d.ts里寫(xiě)這些聲明。 如果你熟悉C/C++,你可以把它們當(dāng)做 .h文件。 讓我們看一些例子。

外部命名空間

流行的程序庫(kù)D3在全局對(duì)象d3里定義它的功能。 因?yàn)檫@個(gè)庫(kù)通過(guò)一個(gè) <script>標(biāo)簽加載(不是通過(guò)模塊加載器),它的聲明文件使用內(nèi)部模塊來(lái)定義它的類型。 為了讓TypeScript編譯器識(shí)別它的類型,我們使用外部命名空間聲明。 比如,我們可以像下面這樣寫(xiě):

D3.d.ts (部分摘錄)

declare namespace D3 {

? ? export interface Selectors {

? ? ? ? select: {

? ? ? ? ? ? (selector: string): Selection;

? ? ? ? ? ? (element: EventTarget): Selection;

? ? ? ? };

? ? }

? ? export interface Event {

? ? ? ? x: number;

? ? ? ? y: number;

? ? }

? ? export interface Base extends Selectors {

? ? ? ? event: Event;

? ? }

}

declare var d3: D3.Base;

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 簡(jiǎn)單示例 下面我們來(lái)整理一下前面的驗(yàn)證器實(shí)現(xiàn),每個(gè)模塊只有一個(gè)命名的導(dǎo)出。 為了編譯,我們必需要在命令行上指定一個(gè)...
    2o壹9閱讀 871評(píng)論 2 50
  • TypeScript知識(shí)點(diǎn) | : 聯(lián)合類型var a:number | string ; a既可以是數(shù)字, 也可...
    Never_Yg閱讀 626評(píng)論 0 1
  • 從ECMAScript 2015開(kāi)始,JavaScript引入了模塊的概念。TypeScript也沿用這個(gè)概念。 ...
    2o壹9閱讀 819評(píng)論 1 47
  • core package 概要:Core是所有其他包的基礎(chǔ)包.它提供了大部分功能包括metadata,templa...
    LOVE小狼閱讀 2,879評(píng)論 0 3
  • { "Unterminated string literal.": "未終止的字符串文本。", "Identifi...
    Elbert_Z閱讀 11,000評(píng)論 0 2

友情鏈接更多精彩內(nèi)容