TypeScript 的學習資料很是多,其中也不乏不少優秀的文章和教程。可是目前爲止沒有一個我特別滿意的。緣由有:前端
所以個人想法是作一套不一樣市面上大多數的 TypeScript 學習教程。以人類認知的角度思考問題,學習 TypeScript,經過通俗易懂的例子和圖片來幫助你們創建 TypeScript 世界觀。 而本篇文章則是這個系列的開篇。git
系列安排:github
目錄未來可能會有所調整。
注意,個人系列文章基本不會講 API,所以須要你有必定的 TypeScript 使用基礎,推薦兩個學習資料。typescript
結合這兩個資料和個人系列教程,掌握 TypeScript 指日可待。編程
接下來,咱們經過幾個方面來從宏觀的角度來看一下 TypeScript。數組
<!-- more -->app
上一節的上帝視角看 TypeScript,咱們從宏觀的角度來對 Typescript 進行了一個展望。之因此把那個放到開頭講是讓你們有一個大致的認識,不想讓你們一葉障目。當你對整個宏觀層面有了必定的瞭解,那麼對 Typescript 的理解就不會錯太多。相反,一開始就是具體的概念和 API,則極可能會讓你喪失都總體的基本判斷。函數
實際上, Typescript 一直在不斷更新迭代。一方面是由於當初許下的諾言」Typescript 是 JavaScript 的超集「(JavaScript 的特性你要同步支持,同時也要處理各類新語法帶來的不兼容狀況)。不單是 ECMA,社區的其餘發展可能也會讓 Typescript 很難受。 好比 JSX 的普遍使用就給 Typescript 泛型的使用帶來了影響。學習
TypeScript 一直處於高速的迭代。除了修復平常的 bug 以外,TypeScript 也在不斷髮布新的功能,好比最新 4.0.0 beta 版本的標籤元祖 的功能就對智能提示這塊頗有用。Typescript 在社區發展方面也作的格外好,以致於它的競爭對手 Flow 被 Typescript 完美擊敗,這在很大程度上就是由於 Typescript 沒有爛尾。現在微軟在開源方向的發力是愈來愈顯著了,我很期待微軟接下來的表現,讓咱們拭目以待。spa
有的同窗可能有疑問, JavaScript 不是也有類型麼? 它和 Typescript 的類型是一回事麼?JavaScript 不是動態語言麼,那麼通過 Typescript 的限定會不會喪失動態語言的動態性呢?咱們繼續往下看。
記住這兩句話,咱們接下來解釋一下這兩句話。
對於 JavaScript 來講,一個變量能夠是任意類型。
var a = 1; a = "lucifer"; a = {}; a = [];
上面的值是有類型的。好比 1 是 number 類型,"lucifer" 是字符串類型, {} 是對象類型, [] 是數組類型。而變量 a 是沒有固定類型的。
對於 Typescript 來講, 一個變量只能接受和它類型兼容的類型的值。提及來比較拗口, 看個例子就明白了。
var a: number = 1; a = "lucifer"; // error var b: any = 1; a = "lucifer"; // ok a = {}; // ok a = []; // ok
咱們不能將 string 類型的值賦值給變量 a, 由於 string 和 number 類型不兼容。而咱們能夠將 string,Object,Array 類型的值賦值給 b,所以 它們和 any 類型兼容。簡單來講就是,一旦一個變量被標註了某種類型,那麼其就只能接受這個類型以及它的子類型。
類型和值居住在不一樣的空間,一個在陽間一個在陰間。他們之間互相不能訪問,甚至不知道彼此的存在。類型不能當作值來用,反之亦然。
以下代碼會報類型找不到的錯:
const aa: User = { name: "lucifer", age: 17 };
這個比較好理解,咱們只須要使用 interface 聲明一下 User 就行。
interface User { name: string; age: number; } const aa: User = { name: "lucifer", age: 17 };
也就是說使用 interface 能夠在類型空間聲明一個類型,這個是 Typescript 的類型檢查的基礎之一。
實際上類型空間內部也會有子空間。咱們能夠用 namespace(老)和 module(新) 來建立新的子空間。子空間之間不能直接接觸,須要依賴導入導出來交互。
好比,我用 Typescript 寫出以下的代碼:
const a = window.lucifer();
Typescript 會報告一個相似Property 'lucifer' does not exist on type 'Window & typeof globalThis'.
的錯誤。
實際上,這種錯誤並非類型錯誤,而是找不到成員變量的錯誤。咱們能夠這樣解決:
declare var lucifer: () => any;
也就是說使用 declare 能夠在值空間聲明一個變量。這個是 Typescript 的變量檢查的基礎,不是本文要講的主要內容,你們知道就行。
明白了 JavaScript 和 TypeScript 類型的區別和聯繫以後,咱們就能夠來進入咱們本文的主題了:類型系統。
TypeScript 官方描述中有一句:TypeScript adds optional types to JavaScript that support tools for large-scale JavaScript applications。實際上這也正是 Typescript 的主要功能,即給 JavaScript 添加靜態類型檢查。要想實現靜態類型檢查,首先就要有類型系統。總之,咱們使用 Typescript 的主要目的仍然是要它的靜態類型檢查,幫助咱們提供代碼的擴展性和可維護性。所以 Typescript 須要維護一套完整的類型系統。
類型系統包括 1. 類型 和 2.對類型的使用和操做,咱們先來看類型。
TypeScript 支持 JavaScript 中全部的類型,而且還支持一些 JavaScript 中沒有的類型(畢竟是超集嘛)。沒有的類型能夠直接提供,也能夠提供自定義能力讓用戶來本身創造。 那爲何要增長 JavaScript 中沒有的類型呢?我舉個例子,好比以下給一個變量聲明類型爲 Object,Array 的代碼。
const a: Object = {}; const b: Array = [];
其中:
上面說了類型和值居住在不一樣的空間,一個在陽間一個在陰間。他們之間互相不能訪問,甚至不知道彼此的存在。
使用 declare 和 interface or type 就是分別在兩個空間編程。好比 Typescript 的泛型就是在類型空間編程,叫作類型編程。除了泛型,還有集合運算,一些操做符好比 keyof 等。值的編程在 Typescript 中更多的體現是在相似 lib.d.ts 這樣的庫。固然 lib.d.ts 也會在類型空間定義各類內置類型。咱們沒有必要去死扣這個,只須要了解便可。
lib.d.ts 的內容主要是一些變量聲明(如:window、document、math)和一些相似的接口聲明(如:Window、Document、Math)。尋找代碼類型(如:Math.floor)的最簡單方式是使用 IDE 的 F12(跳轉到定義)。
TypeScript 要想解決 JavaScript 動態語言類型太寬鬆的問題,就須要:
注意是變量,不是值。
第一個點是經過類型註解的語法來完成。即相似這樣:
const a: number = 1;
Typescript 的類型註解是這樣, Java 的類型註解是另外一個樣子,Java 相似 int a = 1。 這個只是語法差別而已,做用是同樣的。
第二個問題, Typescript 提供了諸如 lib.d.ts 等類型庫文件。隨着 ES 的不斷更新, JavaScript 類型和全局變量會逐漸變多。Typescript 也是採用這種 lib 的方式來解決的。
(TypeScript 提供的部分 lib)
第三個問題,Typescript 主要是經過 interface,type,函數類型等打通類型空間,經過 declare 等打通值空間,並結合 binder 來進行類型診斷。關於 checker ,binder 是如何運做的,能夠參考我第一篇的介紹。
接下來,咱們介紹類型系統的功能,即它能爲咱們帶來什麼。若是上面的內容你已經懂了,那麼接下來的內容會讓你感到」你也不過如此嘛「。
好比定義 String 類型, 以及其原型上的方法和屬性。
length, includes 以及 toString 是 String 的成員變量, 生活在值空間, 值空間雖然不能直接和類型空間接觸,可是類型空間能夠做用在值空間,從而給其添加類型(如上圖黃色部分)。
interface User { name: string; age: number; say(name: string): string; }
這個是我自定義的類型 User,這是 Typescript 必須提供的能力。
這個主要是用來判斷類型是否正確的,上面我已經提過了,這裏就不贅述了。
有時候你不須要顯式說明類型(類型註解),Typescript 也能知道他的類型,這就是類型推導結果。
const a = 1;
如上代碼,編譯器會自動推導出 a 的類型 爲 number。還能夠有連鎖推導,泛型的入參(泛型的入參是類型)推導等。類型推導還有一個特別有用的地方,就是用到類型收斂。
接下來咱們詳細瞭解下類型推導和類型收斂。
let a = 1;
如上代碼。 Typescript 會推導出 a 的類型爲 number。
若是隻會你這麼寫就會報錯:
a = "1";
所以 string 類型的值不能賦值給 number 類型的變量。咱們可使用 Typescript 內置的 typeof 關鍵字來證實一下。
let a = 1; type A = typeof a;
此時 A 的類型就是 number,證實了變量 a 的類型確實被隱式推導成了 number 類型。
有意思的是若是 a 使用 const 聲明,那麼 a 不會被推導爲 number,而是推導爲類型 1。即值只能爲 1 的類型,這就是類型收斂。
const a = 1; type A = typeof a;
經過 const ,咱們將 number 類型收縮到了 值只能爲 1 的類型。
實際狀況的類型推導和類型收斂要遠比這個複雜, 可是作的事情都是一致的。
好比這個:
function test(a: number, b: number) { return a + b; } type A = ReturnType<typeof test>;
A 就是 number 類型。 也就是 Typescript 知道兩個 number 相加結果也是一個 number。所以即便你不顯示地註明返回值是 number, Typescript 也能猜到。這也是爲何 JavaScript 項目不接入 Typescript 也能夠得到類型提示的緣由之一。
除了 const 能夠收縮類型, typeof, instanceof 都也能夠。 緣由很簡單,就是Typescript 在這個時候能夠 100% 肯定你的類型了。 我來解釋一下:
好比上面的 const ,因爲你是用 const 聲明的,所以 100% 不會變,必定永遠是 1,所以類型能夠收縮爲 1。 再好比:
let a: number | string = 1; a = "1"; if (typeof a === "string") { a.includes; }
if 語句內 a 100% 是 string ,不能是 number。所以 if 語句內類型會被收縮爲 string。instanceof 也是相似,原理如出一轍。你們只要記住Typescript 若是能夠 100% 肯定你的類型,而且這個類型要比你定義的或者 Typescript 自動推導的範圍更小,那麼就會發生類型收縮就好了。
本文主要講了 Typescript 的類型系統。 Typescript 和 JavaScript 的類型是很不同的。從表面上來看, TypeScript 的類型是 JavaScript 類型的超集。可是從更深層次上來講,二者的本質是不同的,一個是值的類型,一個是變量的類型。
Typescript 空間分爲值空間和類型空間。兩個空間不互通,所以值不能當成類型,類型不能當成值,而且值和類型不能作運算等。不過 TypeScript 能夠將二者結合起來用,這個能力只有 TypeScript 有, 做爲 TypeScript 的開發者的你沒有這個能力,這個我在第一節也簡單介紹了。
TypeScript 既會對變量存在與否進行檢查,也會對變量類型進行兼容檢查。所以 TypeScript 就須要定義一系列的類型,以及類型之間的兼容關係。默認狀況,TypeScript 是沒有任何類型和變量的,所以你使用 String 等都會報錯。TypeScript 使用庫文件來解決這個問題,最經典的就是 lib.d.ts。
TypeScript 已經作到了足夠智能了,以致於你不須要寫類型,它也能猜出來,這就是類型推導和類型收縮。固然 TypeScript 也有一些功能,咱們以爲應該有,而且也是能夠作到的功能空缺。可是我相信隨着 TypeScript 的逐步迭代(截止本文發佈,TypeScript 剛剛發佈了 4.0.0 的 beta 版本),必定會愈來愈完善,用着愈來愈舒服的。
咱們每一個項目的須要是不同的, 簡單的基本類型確定沒法知足多樣的項目需求,所以咱們必須支持自定義類型,好比 interface, type 以及複雜一點的泛型。固然泛型很大程度上是爲了減小樣板代碼而生的,和 interface , type 這種剛需不太同樣。
有了各類各樣的類型以及類型上的成員變量,以及成員變量的類型,再就加上類型的兼容關係,咱們就能夠作類型檢查了,這就是 TypeScript 類型檢查的基礎。TypeScript 內部須要維護這樣的一個關係,並對變量進行類型綁定,從而給開發者提供類型分析服務。
你們也能夠關注個人公衆號《腦洞前端》獲取更多更新鮮的前端硬核文章,帶你認識你不知道的前端。
公衆號【 力扣加加】
知乎專欄【 Lucifer - 知乎】
點關注,不迷路!