TypeScript 架構概述

本文首先介紹 TS 架構的各個組成,而後是涉及的數據結構,最後會介紹整個編譯過程。git

原文: Architectural Overview

架構分層

TS 架構分層

TS 架構層次如上圖所示,下面將對每層進行分析。github

核心編譯器

核心編譯器位於最底層,它包含如下部分:typescript

  • 語法解析器(Parser):根據 TS 語法,從一系列源文件生成對應的抽象語法樹(AST)。
  • 類型聯合器(Binder):合併同一類型名稱的全部聲明,例如在不一樣文件中的同名接口,這使得類型系統能夠直接使用合併後的類型。
  • 類型檢查器(Checker):解析每種類型結構,檢查語義並生成恰當的檢查結果。
  • 代碼生成器(Emitter):把 .ts.d.ts 文件轉換成 .js.d.ts.map 等文件。
  • 預處理器(Pre-processor):編譯上下文(Compilation)指的是和程序相關的全部文件。編譯器會按序檢查全部傳入的待編譯入口文件,而後會把這些文件中直接或間接 import 的文件,以及 /// <reference path=... /> 指向的文件,歸入到編譯過程,從而構成了最終的編譯上下文。經過遍歷文件索引圖,會獲得一個已排序的源文件列表,這些文件列表就構成了整個應用程序。在解析 import 時,編譯器會優先查找 .ts.d.ts 文件,以確保處理的是最新的文件。編譯器默認使用跟 Node.js 相似的模塊定位方式,它會逐路徑往上查找能匹配到指定模塊名的 .ts.d.ts 文件。若是沒有定位到對應的模塊,編譯器也不必定拋出錯誤,由於該模塊可能在環境模塊中被聲明瞭,好比 path 等 Node.js 內置模塊。

獨立編譯器

獨立編譯器在覈心編譯器的基礎上額外提供了批量編譯命令,它能針對不一樣引擎(如 Node.js)採起不一樣的文件讀寫策略。咱們經過 npm i typescript -g 後,得到的 tsc 命令實際就是這個獨立編譯器。它會處理咱們命令行中指定的文件,而後送入核心編譯器進行編譯。npm

語言服務

語言服務爲核心編譯器封裝了一層接口,尤爲適用於編輯器一類的應用。
語言服務支持典型的編輯器操做,包括:服務器

  1. 自動補全、函數簽名提示、格式化和高亮、着色等
  2. 基本重構功能,如重命名
  3. 調試接口助手,如斷點驗證
  4. TS 特有的增量編譯(--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 語言中包含三種類型簽名:函數調用簽名、構造函數簽名和索引簽名。

編譯過程概述

TS Compiler

整個編譯過程從預處理開始。編碼

1、 預處理器會找出全部 import 語句 和 reference 指令所依賴的文件,並把它們都列爲待編譯文件。

2、 解析器解析全部待編譯文件,生成 AST Node 。這僅僅是以樹的形式來抽象表示待編譯文件。SourceFile 對象除了是表示文件的 AST 外,還有額外的信息,好比文件名、源碼等。不過此時的 SourceFile 並無包含類型信息。

3、 類型聯合器遍歷 AST ,生成並綁定 Symbol 。每一個具名實體類型都會建立一個 Symbol。要注意的是,不一樣的多個聲明節點可能有相同的類型名稱。這就意味着不用的 Node 能夠有相同的 Symbol,每一個 Symbol 會跟蹤全部跟它有關的 Node 。舉例來講,對於相同名稱的 classinterface ,它們的類型會合並,而且指向相同的 Symbol 。類型聯合器也會處理好做用域,以確保每一個 Symbol 處於正確的做用域範圍內。

4、 生成 Symbol 以後,經過調用 createSourceFile 就能夠生成具備 SymbolSourceFile 了。不過,Symbol 表示的是單個文件中的具名實體類型,因爲來自多個文件的同名類型聲明能夠合併,所以下一步須要經過 Program 對象來構建一個囊括全部文件的全局 Symbol 視圖。

5、 Program 使用 createProgram 接口生成,它包括全部 SourceFile 以及 CompilerOptions

6、 針對 Program 建立一個 TypeChecker ,它是 TS 類型系統的核心。它主要負責理清來自多個文件的 Symbol 之間的關係,綁定 TypeSymbol,以及生成語義診斷信息(好比錯誤信息)。具體來講,TypeChecker 作的第一件事是把來自不一樣 SourceFileSymbol 整合到單個視圖中,而後會建立一張 Symbol 表,用來記錄全部的 Symbol ,來自不一樣文件的同名 Symbol 會在這個記錄過程當中完成合並。一旦 TypeChecker 完成初始化,它就能夠處理關於當前 Program 的任何類型問題了,好比:

* 某個 `Node` 的 `Symbol` 是什麼?
* 某個 `Symbol` 的 `Type` 是什麼?
* AST 的某個局部有哪些 `Symbol` 是可見的?
* 某個函數聲明有哪些可用的 `Signature` ?
* 某個文件應該報告哪些錯誤信息?

TypeChecker 的全部檢查都是延遲計算的。若是問它一個問題,它只會檢查與這個問題相關的必要信息。也就是說,它會只檢查與當前問題相關的 NodeSymbolType ,而不會嘗試檢查額外的信息。

7、 最後,針對 Program 也會建立一個代碼生成器,它負責針對給定的 SourceFile 生成預期的代碼文件,包括 .js.jsx.d.ts.js.map 文件。

相關文章
相關標籤/搜索