TypeScript 的學習資料非常多,其中也不乏很多優秀的文章和教程。但是目前為止沒有一個我特別滿意的。原因有:
- 它們大多數沒有一個清晰的主線,而是按照 API 組織章節的,內容在邏輯上比較零散。
- 大多是“講是什麼,怎麼用“,而不是”講為什麼,講原理“。
- 大多數內容比較枯燥,趣味性比較低。都是乾巴巴的文字,沒有圖片,缺乏能夠引起強烈共鳴的例子。
因此我的想法是做一套不同市面上大多數的 TypeScript 學習教程。以人類認知的角度思考問題,學習 TypeScript,通過通俗易懂的例子和圖片來幫助大家建立 TypeScript 世界觀。 而本篇文章則是這個系列的開篇。
系列安排:
- 上帝視角看 TypeScript(就是本文)
- TypeScript 型別系統
- 什麼是 types?什麼是 @types?
- 型別推導, 型別斷言與型別保護
- 你不知道的 TypeScript 泛型(萬字長文,建議收藏)(已釋出)
- TypeScript 練習題
- TypeScript 配置檔案該怎麼寫?
- TypeScript 是如何與 React,Vue,Webpack 整合的?
目錄將來可能會有所調整。
注意,我的系列文章基本不會講 API,因此需要你有一定的 TypeScript 使用基礎,推薦兩個學習資料。
結合這兩個資料和我的系列教程,掌握 TypeScript 指日可待。
接下來,我們通過幾個方面來從巨集觀的角度來看一下 TypeScript。
從輸入輸出上來看
如果我們把 Typescript 編譯器看成一個黑盒的話。其輸入則是使用 TypeScript 語法書寫的文字或者文字集合。
(文字)
如果幾個文字有引用關係,比如 a.ts 依賴 foo.ts 和 bar.ts,其就是一個文字集合。
(文字集合)
輸出是編譯之後的 JS 檔案 和 .d.ts 的宣告檔案。
其中 JS 是將來需要執行的檔案,而 .d.ts 宣告檔案則是 ts 檔案中的型別宣告,這個型別宣告就是你在 ts 檔案中宣告的型別和 TypeScript 型別推導系統推導的型別。當然你也可以自己寫 .d.ts 宣告檔案。
從功能上來看
從巨集觀的視角來看,TypeScript 的功能就是:
- 提供了豐富的型別系統。
最簡單的就是 變數名:型別 = 值
const a: Number = 1;
除了這些基本型別,還提供了函式型別,複合型別等。
- 提供了型別操作 API。TypeScript 不但提供內建型別,使用者也可以利用集合操作和泛型對型別操作從而生成新的型別。
- 對每一種型別的屬性和方法都進行了定義。
比如 String 型別有 toString 方法,但是沒有 toFixed 方法,這就是 lib.d.ts 定義的。這樣我在 String 型別的變數上使用 toFixed 方法就會報錯,達到了“型別檢查”的作用。
小提示:lib.d.ts 的內容主要是一些變數宣告(如:window、document、math)和一些類似的介面宣告(如:Window、Document、Math)。 你可以通過 --noLib 來關閉這一功能
- 提供了模組系統(module,namespace)。
- 提供了更加方面的 API,比如 class(這在 ES6 class 出來之前尤其好用),裝飾器等。
- 。。。
TypeScript 編譯器是如何工作的?
上面已經討論了 TypeScript 編譯器的輸入和輸出。那黑盒內部是怎麼工作呢?這裡我簡單介紹一下:
- TypeScript 文字首先會被解析為 token 流。這個過程比較簡單,就是單純地按照分隔符去分割文字即可。
- 接著 token 流會被轉換為 AST,也就是抽象語法樹。
- binder 則根據 AST 資訊生成 Symbol(TypeScript 中的一個資料結構)。拿上面的圖來說,就是 number 節點。
- 當我們需要型別檢查的時候, checker 會根據前面生成的 AST 和 symbols 生成型別檢查結果。
- 當我們需要生成 JS 檔案的時候,emitter 同樣會根據前面生成的 AST 和 symbols 生成 JS 檔案。
完整圖:
總結
總的來說,TypeScript 就是一門語言,和 Java,Python,C++ 等類似。只不過這門語言主要目標就是為了彌補 JavaScript 弱型別帶來的問題的。因此設計語言的出發點就是:
- 靜態型別系統
- 可以編譯成 JavaScript
因此 TypeScript 是一門最終編譯為 JavaScript 的語言(當然還有型別檔案)。既然是一門語言,就涉及詞法分析,語法分析等流程。由於相對 JavaScript 增加了很多功能, 其中最主要的就是型別系統。因此 TypeScript 的分析工作要比 JavaScript 更加複雜, 集中體現在 binder 和 checker 部分。
由於提供了靜態型別, 因此就需要提供一些內建型別給我們用,比如 number,string,Array 等。但是這並不能滿足我們的所有需求,我們需要自定義型別,因此有了 type,有了 interface 等。後來我們又發現自定義的型別重複程式碼太多, 要是型別也可以通過程式設計生成新的型別就好了,於是有了集合運算和泛型。
程式碼都放到一起不方便維護,要是可以放到不同檔案,需要用的時候組裝起來就好了,於是有了模組化。我用了別人的用 TypeScript 開發的庫,如果也能有型別校驗就好了,於是有了 types。
。。。
其實這些都是有因果關係的,如果你可以牢牢地掌握這些因果關係,那麼學起來還不是易如反掌?
相關閱讀
- TypeScript 編譯原理
- Bring your own TypeScript with more internal definitions
- Compiler Internals
- TypeScript 編譯器是用 TypeScript 寫的,那是先有編譯器還是 TS?
點關注,不迷路
大家也可以關注我的公眾號《腦洞前端》獲取更多更新鮮的前端硬核文章,帶你認識你不知道的前端。
知乎專欄【 Lucifer - 知乎】