鍍金池/ 教程/ HTML/ 架構(gòu)概述
初始化項目結(jié)構(gòu)
聯(lián)合類型
介紹
介紹
介紹
編譯選項
TypeScript 1.6
介紹
介紹
發(fā)展路線圖
介紹
在MSBuild里使用編譯選項
可迭代性
TypeScript 1.3
介紹
介紹
TypeScript 1.1
變量聲明
即將到來的Angular 2框架是使用TypeScript開發(fā)的。 因此Angular和TypeScript一起使用非常簡單方便
tsconfig.json
介紹
介紹
介紹
在MSBuild里使用編譯選項
使用TypeScript的每日構(gòu)建版本
新建工程
枚舉
三斜線指令
結(jié)合ASP.NET v5使用TypeScript
TypeScript里的this
介紹
TypeScript 1.4
編碼規(guī)范
介紹
模塊解析
ASP.NET 4
架構(gòu)概述
介紹
介紹
ASP.NET Core
TypeScript 1.8
介紹
介紹
創(chuàng)建簡單工程
TypeScript 1.7
TypeScript 1.5
NPM包的類型
支持TypeScript的編輯器

架構(gòu)概述

層次概述

Architectural overview.

  • 核心TypeScript編譯器

    • 語法分析器(Parser): 以一系列原文件開始, 根據(jù)語言的語法, 生成抽象語法樹(AST)

    • 聯(lián)合器(Binder): 使用一個Symbol將針對相同結(jié)構(gòu)的聲明聯(lián)合在一起(例如:同一個接口或模塊的不同聲明,或擁有相同名字的函數(shù)和模塊)。這能幫助類型系統(tǒng)推導(dǎo)出這些具名的聲明。

    • 類型解析器與檢查器(Type resolver / Checker): 解析每種類型的構(gòu)造,檢查讀寫語義并生成適當(dāng)?shù)脑\斷信息。

    • 生成器(Emitter): 從一系列輸入文件(.ts和.d.ts)生成輸出,它們可以是以下形式之一:JavaScript(.js),聲明(.d.ts),或者是source maps(.js.map)。

    • 預(yù)處理器(Pre-processor): “編譯上下文”指的是某個“程序”里涉及到的所有文件。上下文的創(chuàng)建是通過檢查所有從命令行上傳入編譯器的文件,按順序,然后再加入這些文件直接引用的其它文件或通過import語句和/// <reference path=... />標(biāo)簽間接引用的其它文件。

沿著引用圖走下來你會發(fā)現(xiàn)它是一個有序的源文件列表,它們組成了整個程序。

當(dāng)解析導(dǎo)出(import)的時候,會優(yōu)先選擇“.ts”文件而不是“.d.ts”文件,以確保處理的是最新的文件。 編譯器會進(jìn)行與Nodejs相似的流程來解析導(dǎo)入,沿著目錄鏈查找與將要導(dǎo)入相匹配的帶.ts或.d.ts擴(kuò)展名的文件。 導(dǎo)入失敗不會報error,因為可能已經(jīng)聲明了外部模塊。

  • 獨(dú)立編譯器(tsc): 批處理編譯命令行界面。主要處理針對不同支持的引擎讀寫文件(比如:Node.js)。

  • 語言服務(wù): “語言服務(wù)”在核心編譯器管道上暴露了額外的一層,非常適合類編輯器的應(yīng)用。

語言服務(wù)支持一系列典型的編輯器操作比如語句自動補(bǔ)全,函數(shù)簽名提示,代碼格式化和突出高亮,著色等。

基本的重構(gòu)功能比如重命名,調(diào)試接口輔助功能比如驗證斷點(diǎn),還有TypeScript特有的功能比如支持增量編譯(在命令行上使用--watch)。 語言服務(wù)是被設(shè)計用來有效的處理在一個長期存在的編譯上下文中文件隨著時間改變的情況;在這樣的情況下,語言服務(wù)提供了與其它編譯器接口不同的角度來處理程序和源文件。

請參考 [[Using the Language Service API]] 以了解更多詳細(xì)內(nèi)容。

數(shù)據(jù)結(jié)構(gòu)

  • Node: 抽象語法樹(AST)的基本組成塊。通常Node表示語言語法里的非終結(jié)符;一些終結(jié)符保存在語法樹里比如標(biāo)識符和字面量。

  • SourceFile: 給定源文件的AST。SourceFile本身是一個Node;它提供了額外的接口用來訪問文件的源碼,文件里的引用,文件里的標(biāo)識符列表和文件里的某個位置與它對應(yīng)的行號與列號的映射。

  • Program: SourceFile的集合和一系列編譯選項代表一個編譯單元。Program是類型系統(tǒng)和生成代碼的主入口。

  • Symbol: 具名的聲明。Symbols是做為聯(lián)合的結(jié)果而創(chuàng)建。Symbols連接了樹里的聲明節(jié)點(diǎn)和其它對同一個實體的聲明。Symbols是語義系統(tǒng)的基本構(gòu)建塊。

  • Type: Type是語義系統(tǒng)的其它部分。Type可能被命名(比如,類和接口),或匿名(比如,對象類型)。

  • Signature: 一共有三種Signature類型:調(diào)用簽名(call),構(gòu)造簽名(construct)和索引簽名(index)。

編譯過程概述

整個過程從預(yù)處理開始。 預(yù)處理器會算出哪些文件參與編譯,它會去查找如下引用(/// <reference path=... />標(biāo)簽和import語句)。

語法分析器(Parser)生成抽象語法樹(AST)Node. 這些僅為用戶輸出的抽象表現(xiàn),以樹的形式。 一個SourceFile對象表示一個給定文件的AST并且?guī)в幸恍╊~外的信息如文件名及源文件內(nèi)容。

然后,聯(lián)合器(Binder)處理AST節(jié)點(diǎn),結(jié)合并生成Symbols。 一個Symbol會對應(yīng)到一個命名實體。 這里有個一微妙的差別,幾個聲明節(jié)點(diǎn)可能會是名字相同的實體。 也就是說,有時候不同的Node具有相同的Symbol,并且每個Symbol保持跟蹤它的聲明節(jié)點(diǎn)。 比如,一個名字相同的classnamespace可以合并,并且擁有相同的Symbol。 聯(lián)合器也會處理作用域,以確保每個Symbol都在正確的封閉作用域里創(chuàng)建。

生成SourceFile(還帶有它的Symbols們)是通過調(diào)用createSourceFile API。

到目前為止,Symbol代表的命名實體可以在單個文件里看到,但是有些聲明可以從多文件合并,因此下一步就是構(gòu)建一個全局的包含所有文件的視圖,也就是創(chuàng)建一個Program

一個ProgramSourceFile的集合并帶有一系列CompilerOptions。 通過調(diào)用createProgram API來創(chuàng)建Program

通過一個Program實例創(chuàng)建TypeChecker。 TypeChecker是TypeScript類型系統(tǒng)的核心。 它負(fù)責(zé)計算出不同文件里的Symbols之間的關(guān)系,將Type賦值給Symbol,并生成任何語義Diagnostic(比如:error)。

TypeChecker首先要做的是合并不同的SourceFile里的Symbol到一個單獨(dú)的視圖,創(chuàng)建單一的Symbol表,合并所有普通的Symbol(比如:不同文件里的namespace)。

在原始狀態(tài)初始化完成后,TypeChecker就可以解決關(guān)于這個程序的任何問題了。 這些“問題”可以是:

  • 這個NodeSymbol是什么?
  • 這個SymbolType是什么?
  • 在AST的某個部分里有哪些Symbol是可見的?
  • 某個函數(shù)聲明的Signature都有哪些?
  • 針對某個文件應(yīng)該報哪些錯誤?

TypeChecker計算所有東西都是“懶惰的”;為了回答一個問題它僅“解決”必要的信息。 TypeChecker僅會檢測和這個問題有關(guān)的Node,SymbolType,不會檢測額外的實體。

對于一個Program同樣會生成一個Emitter。 Emitter負(fù)責(zé)生成給定SourceFile的輸出;它包括:.js,.jsx.d.ts.js.map。

術(shù)語

完整開始/令牌開始(Full Start/Token Start)

令牌本身就具有我們稱為一個“完整開始”和一個“令牌開始”?!傲钆崎_始”是指更自然的版本,它表示在文件中令牌開始的位置?!巴暾_始”是指從上一個有意義的令牌之后掃描器開始掃描的起始位置。當(dāng)關(guān)心瑣事時,我們往往更關(guān)心完整開始。

函數(shù) 描述
ts.Node.getStart 取得某節(jié)點(diǎn)的第一個令牌起始位置。
ts.Node.getFullStart 取得某節(jié)點(diǎn)擁有的第一個令牌的完整開始。

瑣碎內(nèi)容(Trivia)

語法的瑣碎內(nèi)容代表源碼里那些對理解代碼無關(guān)緊要的內(nèi)容,比如空白,注釋甚至一些沖突的標(biāo)記。

因為瑣碎內(nèi)容不是語言正常語法的一部分(不包括ECMAScript API規(guī)范)并且可能在任意2個令牌中的任意位置出現(xiàn),它們不會包含在語法樹里。但是,因為它們對于像重構(gòu)和維護(hù)高保真源碼很重要,所以需要的時候還是能夠通過我們的APIs訪問。

因為EndOfFileToken后面可以沒有任何內(nèi)容(令牌和瑣碎內(nèi)容),所有瑣碎內(nèi)容自然地在非瑣碎內(nèi)容之前,而且存在于那個令牌的“完整開始”和“令牌開始”之間。

雖然這個一個方便的標(biāo)記法來說明一個注釋“屬于”一個Node。比如,在下面的例子里,可以明顯看出genie函數(shù)擁有兩個注釋:

var x = 10; // This is x.

/**
 * Postcondition: Grants all three wishes.
 */
function genie([wish1, wish2, wish3]: [Wish, Wish, Wish]) {
    while (true) {
    }
} // End function

這是盡管事實上,函數(shù)聲明的完整開始是在var x = 10;后。

我們依據(jù)處理注釋所有權(quán)。通常來講,一個令牌擁有同一行上的所有的瑣碎內(nèi)容直到下一個令牌開始。任何出現(xiàn)在這行之后的注釋都屬于下一個令牌。源文件的第一個令牌擁有所有的初始瑣碎內(nèi)容,并且最后面的一系列瑣碎內(nèi)容會添加到end-of-file令牌上。

對于大多數(shù)普通用戶,注釋是“有趣的”瑣碎內(nèi)容。屬于一個節(jié)點(diǎn)的注釋內(nèi)容可以通過下面的函數(shù)來獲?。?/p>

函數(shù) 描述
ts.getLeadingCommentRanges 提供源文件和一個指定位置,返回指定位置后的第一個換行與令牌之間的注釋的范圍(與ts.Node.getFullStart配合會更有用)。
ts.getTrailingCommentRanges 提供源文件和一個指定位置,返回到指定位置后第一個換行為止的注釋的范圍(與ts.Node.getEnd配合會更有用)。

做為例子,假設(shè)有下面一部分源代碼:

debugger;/*hello*/
    //bye
  /*hi*/    function

function關(guān)鍵字的完整開始是從/*hello*/注釋,但是getLeadingCommentRanges僅會返回后面2個注釋:

d e b u g g e r ; / * h e l l o * / _ _ _ _ _ [CR] [NL] _ _ _ _ / / b y e [CR] [NL] _ _ / * h i * / _ _ _ _ f u n c t i o n
                  ↑                                     ↑       ↑                       ↑                   ↑
                  完整開始                              查找      第一個注釋               第二個注釋     令牌開始
                                                       開始注釋

適當(dāng)?shù)?,?code>debugger語句后調(diào)用getTrailingCommentRanges可以提取出/*hello*/注釋。

如果你關(guān)心令牌流的更多信息,createScanner也有一個skipTrivia標(biāo)記,你可以設(shè)置成false,然后使用setText/setTextPos來掃描文件里的不同位置。