【Typescript 類型檢查原理】類型守衛(wèi)是如何實(shí)現(xiàn)的
本文轉(zhuǎn)載自微信公眾號「神光的編程秘籍」,作者神說要有光zxg。轉(zhuǎn)載本文請聯(lián)系神光的編程秘籍公眾號。
這一節(jié)我們來理一下類型守衛(wèi)的實(shí)現(xiàn)原理,因?yàn)閮?nèi)容比較多,分為上下兩篇,上篇講實(shí)現(xiàn)思路,下篇是代碼實(shí)現(xiàn)。
什么是類型守衛(wèi)
javascript 的類型代表了一種可能性,表示可能占用的內(nèi)存大小、可能調(diào)用的方法等。typescript 的類型包含了 javascript 的類型,并且對可以對類型做交集、并集、各種推導(dǎo),最終產(chǎn)生準(zhǔn)確的類型。typescript 的類型的推導(dǎo)也是一種可能性的推導(dǎo),目標(biāo)是得出的類型更準(zhǔn)確的描述具體的變量類型。
精準(zhǔn)就意味著要做一些類型的可能性的縮小,各種類型編程的目的都是產(chǎn)生更小更準(zhǔn)確的類型,類型守衛(wèi)也是這個(gè)目的。
類型推導(dǎo)是使得整個(gè)類型變得更小更準(zhǔn)確,而類型守衛(wèi)則是當(dāng)類型進(jìn)入某個(gè)分支的時(shí)候,暫時(shí)性的變得更小更精確,使得類型檢查更準(zhǔn)確。
比如下面的代碼,整體類型是 string| number,這是一個(gè)聯(lián)合類型,當(dāng) a 進(jìn)入 if 分支的時(shí)候,類型明顯只可能是 string,別的情況進(jìn)不來,這時(shí)候可以做進(jìn)一步的類型縮小,這就叫做類型守衛(wèi)。
- function func(a: string| number): string {
- if (typeof a === 'string') {
- return a.toLocaleLowerCase();
- } else {
- return a.toFixed(1);
- }
- }
類型守衛(wèi)的目的就是讓整體的類型在一些確定的條件下暫時(shí)性的變得更小更精確。這種條件包括 typeof、instanceOf、in、===、!==、==、!=。
為什么這些條件下可以縮小類型呢?因?yàn)槟軌蜻M(jìn)入這些分支,那么變量顯然只可能是改種類型,所以類型的可能性自然可以做進(jìn)一步的縮小。
比如 in 操作符觸發(fā)的類型守衛(wèi):
=== 判斷觸發(fā)的類型守衛(wèi):
同理 instanceof 等也是一樣,只要是進(jìn)入能夠確定具體類型的分支,那么類型就可以做縮小。
在 ts 4.3 中,泛型的類型縮小也做了支持(之前只能通過類型斷言來縮小類型)。
類型縮小是自動的類型斷言,當(dāng)有的時(shí)候類型縮小或者類型推導(dǎo)都不行的時(shí)候,就用 as 手動類型斷言。
實(shí)現(xiàn)思路分析
我們知道了類型縮小是在在進(jìn)入條件分支的時(shí)候,對類型檢查用的類型做暫時(shí)性的縮小,那么實(shí)現(xiàn)的時(shí)候自然就是在 if、switch 的分支的檢查時(shí),對類型做一些處理。
ts 的類型檢查是先通過解析配置文件的 includes、exclues、files 等,結(jié)合 lib、types、typeRoots 的配置來確定要做檢查的所有文件,然后對每個(gè)文件依次進(jìn)行遞歸下降的類型檢查。
當(dāng)檢查到 if、switch 的節(jié)點(diǎn)的時(shí)候,我們只需要判斷 test 部分是否是一個(gè) BinaryExpression,并且 operator 是 in、===、!==、instanceOf 等情況。
根據(jù) operator 的不同分別做不同的判斷:
- in:判斷 left 是否是 right 變量的類型的一個(gè)屬性,如果是,對類型做縮小
- instanceof:判斷 left 的類型是否是 right 變量的類型的子類型,如果是,對類型做縮小
- === / !==: 分為包含 typeof 和不包含 typeof 兩種:
- 不包含 typeof:判斷 left 和 right 是否相等,如果是,把類型縮小到具體的字面量類型。
- 包含 typeof:如果兩邊有一邊是 typeof 的 UnaryExpression,則取類型之后再做比較,如果是,把類型縮小到具體的類型
總結(jié)
typscript 的高級類型的推導(dǎo)的目的就是縮小可能性范圍,讓類型更精確。有的時(shí)候,在進(jìn)入一些分支的時(shí)候,類型就確定了,這時(shí)候就可以暫時(shí)性的對類型做范圍的縮小,這叫做類型守衛(wèi)。
觸發(fā)條件有 in、instanceof、typeof、===、!== 等能夠讓類型更準(zhǔn)確的判斷。
類型守衛(wèi)相當(dāng)于自動的類型斷言,當(dāng)類型守衛(wèi)搞不定的時(shí)候,就手動用類型斷言 as 來縮小類型。
我們梳理了實(shí)現(xiàn)類型守衛(wèi)的思路,就是遇到條件語句 IfStatement、SwitchStatement 的時(shí)候,對 test 部分做判斷,如果包含 in、instanceof、typeof 等,做相應(yīng)的類型處理,之后再進(jìn)行類型檢查。