本文首先介紹 TS 架構的各個組成,而後是涉及的數據結構,最後會介紹整個編譯過程。git
原文: Architectural Overview
TS 架構層次如上圖所示,下面將對每層進行分析。github
核心編譯器位於最底層,它包含如下部分:typescript
.ts
和 .d.ts
文件轉換成 .js
、.d.ts
和 .map
等文件。import
的文件,以及 /// <reference path=... />
指向的文件,歸入到編譯過程,從而構成了最終的編譯上下文。經過遍歷文件索引圖,會獲得一個已排序的源文件列表,這些文件列表就構成了整個應用程序。在解析 import
時,編譯器會優先查找 .ts
和 .d.ts
文件,以確保處理的是最新的文件。編譯器默認使用跟 Node.js 相似的模塊定位方式,它會逐路徑往上查找能匹配到指定模塊名的 .ts
或 .d.ts
文件。若是沒有定位到對應的模塊,編譯器也不必定拋出錯誤,由於該模塊可能在環境模塊中被聲明瞭,好比 path
等 Node.js 內置模塊。獨立編譯器在覈心編譯器的基礎上額外提供了批量編譯命令,它能針對不一樣引擎(如 Node.js)採起不一樣的文件讀寫策略。咱們經過 npm i typescript -g
後,得到的 tsc
命令實際就是這個獨立編譯器。它會處理咱們命令行中指定的文件,而後送入核心編譯器進行編譯。npm
語言服務爲核心編譯器封裝了一層接口,尤爲適用於編輯器一類的應用。
語言服務支持典型的編輯器操做,包括:服務器
--watch
)語言服務被設計用來專門處理這樣的場景:在長時間存在的編譯上下文中,源文件會隨着時間不斷的變化。
從這個角度來講,相比於市面上的其餘編譯器接口,語言服務在對待程序和源碼的處理方式上提供了較爲不一樣視角。數據結構
附: 詳細的語言服務 API 使用文檔
獨立服務器 tsserver
對編譯器和語言服務層進行了封裝,對外暴露了一種基於 JSON 協議的接口,稱爲語言服務協議(LSP)。架構
附:詳細的獨立服務器 文檔
VS Code 就是一個典型的使用語言服務的編輯器,它經過 LSP 來和語言服務通訊,從而實現良好的編碼體驗。編輯器
TS 編譯器中使用到的主要數據結構有如下 6 類:函數
Node
: AST 的基本構建單元塊。一般來講,Node
表明了語法中的非終端節點。與非終端節點相對的終端節點,好比標識符、字面量等,也在 AST 中。SourceFile
:對應源文件的 AST 。SourceFile
自己是一個 Node
,它額外提供了一些接口,用於訪問包括原始文本、文件包含的引用、標識符列表,以及字符位置映射。Program
:編譯單元的全部 SourceFile
和編譯選項的集合。它是類型系統和代碼生成的主要入口。Symbol
:已命名的聲明,由類型聯合器所生成。它鏈接了 AST 中的聲明節點和其餘地方的同名聲明實體。它是語義系統的基本構建單元塊。Type
: 它是語義系統的另外一部分,它能夠是具名的(如類、接口),也能夠是匿名的(如對象字面量)。Signature
: TS 語言中包含三種類型簽名:函數調用簽名、構造函數簽名和索引簽名。
整個編譯過程從預處理開始。編碼
1、 預處理器會找出全部 import
語句 和 reference
指令所依賴的文件,並把它們都列爲待編譯文件。
2、 解析器解析全部待編譯文件,生成 AST Node
。這僅僅是以樹的形式來抽象表示待編譯文件。SourceFile
對象除了是表示文件的 AST 外,還有額外的信息,好比文件名、源碼等。不過此時的 SourceFile
並無包含類型信息。
3、 類型聯合器遍歷 AST ,生成並綁定 Symbol
。每一個具名實體類型都會建立一個 Symbol
。要注意的是,不一樣的多個聲明節點可能有相同的類型名稱。這就意味着不用的 Node
能夠有相同的 Symbol
,每一個 Symbol
會跟蹤全部跟它有關的 Node
。舉例來講,對於相同名稱的 class
和 interface
,它們的類型會合並,而且指向相同的 Symbol
。類型聯合器也會處理好做用域,以確保每一個 Symbol
處於正確的做用域範圍內。
4、 生成 Symbol
以後,經過調用 createSourceFile
就能夠生成具備 Symbol
的 SourceFile
了。不過,Symbol
表示的是單個文件中的具名實體類型,因爲來自多個文件的同名類型聲明能夠合併,所以下一步須要經過 Program
對象來構建一個囊括全部文件的全局 Symbol
視圖。
5、 Program
使用 createProgram
接口生成,它包括全部 SourceFile
以及 CompilerOptions
。
6、 針對 Program
建立一個 TypeChecker
,它是 TS 類型系統的核心。它主要負責理清來自多個文件的 Symbol
之間的關係,綁定 Type
到 Symbol
,以及生成語義診斷信息(好比錯誤信息)。具體來講,TypeChecker
作的第一件事是把來自不一樣 SourceFile
的 Symbol
整合到單個視圖中,而後會建立一張 Symbol
表,用來記錄全部的 Symbol
,來自不一樣文件的同名 Symbol
會在這個記錄過程當中完成合並。一旦 TypeChecker
完成初始化,它就能夠處理關於當前 Program
的任何類型問題了,好比:
* 某個 `Node` 的 `Symbol` 是什麼? * 某個 `Symbol` 的 `Type` 是什麼? * AST 的某個局部有哪些 `Symbol` 是可見的? * 某個函數聲明有哪些可用的 `Signature` ? * 某個文件應該報告哪些錯誤信息?
TypeChecker
的全部檢查都是延遲計算的。若是問它一個問題,它只會檢查與這個問題相關的必要信息。也就是說,它會只檢查與當前問題相關的 Node
、Symbol
和 Type
,而不會嘗試檢查額外的信息。
7、 最後,針對 Program
也會建立一個代碼生成器,它負責針對給定的 SourceFile
生成預期的代碼文件,包括 .js
、.jsx
、.d.ts
和 .js.map
文件。