本系列目的: 列出TypeScript與JavaScript的不同點, 縮小文件內容, 提高學習速度. 原文件地址: https://www.tslang.cn/index.html

Symbol, 迭代器和生成器, 模組與原生js重合度太高不整理

名稱空間

簡單使用

解決問題; 我們需要一種手段來組織程式碼,以便於在記錄它們型別的同時還不用擔心與其它物件產生命名衝突。 因此,我們把程式碼包裹到一個名稱空間內,而不是把它們放在全域性名稱空間下

例子, 未使用名稱空間, 定義幾個簡單的字串驗證器

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);
    }
}

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

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

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 }'.`);
    }
}

使用關鍵字namespace, 用名稱空間重寫上面程式碼, 並使用關鍵字export暴露出功能

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);
        }
    }
}

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

// 由於是在名稱空間之外訪問,因此需要限定型別的名稱,比如 Validation.StringValidator
let validators: { [s: string]: Validation.StringValidator; } = {}; 
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();

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

分離檔案

解決問題: 當應用變得越來越大時,我們需要將程式碼分離到不同的檔案中以便於維護。

現在,我們把Validation名稱空間分割成多個檔案。 儘管是不同的檔案,它們仍是同一個名稱空間,並且在使用的時候就如同它們在一個檔案中定義的一樣。 因為不同檔案之間存在依賴關係,所以我們加入了引用標籤來告訴編譯器檔案之間的關聯。 我們的測試程式碼保持不變。

a.ts檔案

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

b.ts檔案

// 此處為三斜線指令, 文章末尾有介紹
/// <reference path="a.ts" />
namespace Validation {
    const lettersRegexp = /^[A-Za-z]+$/;
    export class LettersOnlyValidator implements StringValidator {
        isAcceptable(s: string) {
            return lettersRegexp.test(s);
        }
    }
}

c.ts檔案

/// <reference path="a.ts" />
namespace Validation {
    const numberRegexp = /^[0-9]+$/;
    export class ZipCodeValidator implements StringValidator {
        isAcceptable(s: string) {
            return s.length === 5 && numberRegexp.test(s);
        }
    }
}

d.ts檔案

/// <reference path="a.ts" />
/// <reference path="b.ts" />
/// <reference path="c.ts" />

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 }`);
    }

當涉及到多檔案時,我們必須確保所有編譯後的程式碼都被載入了。 我們有兩種方式。
第一種方式,把所有的輸入檔案編譯為一個輸出檔案,需要使用–outFile標記:

tsc --outFile 輸出檔名.js 入口檔案.ts
tsc --outFile gkd.js d.ts

第二種方式,我們可以編譯每一個檔案(預設方式),那麼每個原始檔都會對應生成一個JavaScript檔案。 然後,在頁面上通過<script>標籤把所有生成的JavaScript檔案按正確的順序引進來,比如:

   <script src="a.js" type="text/javascript" />
    <script src="b.js" type="text/javascript" />
    <script src="c.js" type="text/javascript" />
    <script src="d.js" type="text/javascript" />

別名

namespace Shapes {
  export namespace Polygons {
    export function gkd ():void {
      num++;
    }
    export let num = 0;
  }
}

import polygons = Shapes.Polygons;
let sq = polygons.num; // 等同於 "Shapes.Polygons.num"
console.log(sq); // 0

polygons.gkd();
console.log(sq); // 0

注意: 此import不同於es2015的import, es2015的import拿的是值的引用, 而這裡時賦值操作!!!, 從下面編譯後的程式碼中可見一斑

var Shapes;
(function (Shapes) {
    var Polygons;
    (function (Polygons) {
        function gkd() {
            Polygons.num++;
        }
        Polygons.gkd = gkd;
        Polygons.num = 0;
    })(Polygons = Shapes.Polygons || (Shapes.Polygons = {}));
})(Shapes || (Shapes = {}));
var polygons = Shapes.Polygons; // 賦值操作
var sq = polygons.num;
console.log(sq);
polygons.gkd();
console.log(sq);

使用其它的JavaScript庫

為了描述不是用TypeScript編寫的類庫的型別,我們需要宣告類庫匯出的API。 由於大部分程式庫只提供少數的頂級物件,名稱空間是用來表示它們的一個好辦法

我們稱其為宣告是因為它不是外部程式的具體實現。 我們通常在 .d.ts裡寫這些宣告。 如果你熟悉C/C++,你可以把它們當做 .h檔案


三斜線指令

三斜線指令是包含單個XML標籤的單行註釋。 註釋的內容會做為編譯器指令使用
三斜線指令僅可放在包含它的檔案的最頂端。 一個三斜線指令的前面只能出現單行或多行註釋,這包括其它的三斜線指令。 如果它們出現在一個語句或宣告之後,那麼它們會被當做普通的單行註釋,並且不具有特殊的涵義

1. /// <reference path="..." />

該指令是三斜線指令中最常見的一種。 它用於宣告檔案間的依賴

2. /// <reference types="..." />

該指令則聲明瞭對某個包的依賴, 對這些包的名字的解析與在 import語句裡對模組名的解析類似。 可以簡單地把三斜線型別引用指令當做 import宣告的包

例如,把 /// <reference types="node" />引入到宣告檔案,表明這個檔案使用了 @types/node/index.d.ts裡面宣告的名字; 並且,這個包需要在編譯階段與宣告檔案一起被包含進來
僅當在你需要寫一個d.ts檔案時才使用這個指令

3. /// <reference no-default-lib="true"/>

這個指令把一個檔案標記成預設庫。 你會在lib.d.ts檔案和它不同的變體的頂端看到這個註釋。

這個指令告訴編譯器在編譯過程中不要包含這個預設庫(比如,lib.d.ts)。 這與在命令列上使用 --noLib相似。

還要注意,當傳遞了--skipDefaultLibCheck時,編譯器只會忽略檢查帶有/// <reference no-default-lib="true"/>的檔案。