TypeScript类型推论(Type Inference)
要完全理解類型推論需要完整理解類型上下文,并且理解TS對于是否可以使用類型推論是基于靜態(tài)分析完成的。
上下文類型應(yīng)用在許多地方。常見的例子包括函數(shù)調(diào)用的參數(shù),賦值的右手端位置,類型斷言,對象和數(shù)組的成員,和返回語句。上下文類型還充當(dāng)最佳公共類型中的候選類型。
TS中需要為每個JS名字規(guī)定類型,而名字出現(xiàn)在對應(yīng)的上下文中則會自動獲得類型,若沒有對應(yīng)的上下文,這個名字則會自動獲得類型any。
名字:通過聲明語句聲明的名字,例如var、let、const、function a() {}、class A {}、import A from ‘.a’、函數(shù)參數(shù)等,都會在JS環(huán)境中添加一個名字,而TS可以給這個名字指定類型。
在JS中名字的聲明是可以在上面提到的常見例子指定的位置,函數(shù)參數(shù)調(diào)用、賦值右手端位置、對象數(shù)組成員、返回語句。
函數(shù)調(diào)用的參數(shù)
interface Cb {(a: number): void; } interface Fn {(cb: Cb): void; }const fn: Fn = function (cb) {}fn(function (a) { // 這里a的類型是numberconsole.log(a + 1) })因?yàn)閒n這個名字是類型Fn,而Fn類型的入?yún)⑹穷愋虲b,所以在fn使用匿名函數(shù)作為入?yún)⒄{(diào)用的時候TS可以知道這個匿名函數(shù)對應(yīng)的位置是類型Cb,換句話說匿名函數(shù)當(dāng)前的類型上下文是Cb。而Cb要求入?yún)⑹莕umber類型,所以推斷出匿名函數(shù)的參數(shù)a是number類型。
賦值的右手端位置
interface Cb {(a: number): void; }const fn: Cb = function (a) {console.log(a + 1) }fn是類型Cb,因?yàn)镃b要求入?yún)⑹莕umber類型,所以TS推斷出對應(yīng)位置匿名函數(shù)的入?yún)⑹莕umber類型。
對象和數(shù)組的成員
interface Cb {(a: number): void; } interface Obj {fn: Cb } const obj: Obj = {fn: function (a) {console.log(a + 1)} }obj是類型Obj,而Obj具有屬性fn是類型Cb。因?yàn)镃b要求入?yún)⑹莕umber類型,所以TS推斷出對象obj.fn右手端對應(yīng)位置匿名函數(shù)的入?yún)⑹莕umber類型。
返回語句
interface Cb {(a: number): void }interface Fn {(): Cb }const fn: Fn = function () {return (a) => {console.log(a + 1)} }在這例子里,返回的匿名函數(shù)獲得類型上下文Cb,而Cb要求入?yún)⑹莕umber,所以匿名函數(shù)的入?yún)⒈煌茢喑鍪莕umber類型。
上面的幾種類型都可以認(rèn)為名字出現(xiàn)在了賦值的右手端,而被復(fù)制的名字可以給這個值提供對應(yīng)的類型上下文。
類型斷言
interface Fn {(a: number): void; }const fn = function (a) {console.log(a + 1); } as Fn;在這里匿名函數(shù)賦值給變量fn而fn本身是沒有類型的,所以沒辦法推斷匿名函數(shù)的入?yún)的類型,但是我們使用類型給這個匿名函數(shù)指定了類型上下文Fn,讓TS具有了推算參數(shù)a的依據(jù),得出參數(shù)a是number類型。
小結(jié)
類型推斷起作用的條件是名字出現(xiàn)在對應(yīng)的上下文位置,而這個上下文可以通過賦值操作的左手端提供,也可以使用類型斷言直接提供。這樣TS可以根據(jù)對應(yīng)的類型推斷出對應(yīng)變量的類型。
一些意外
interface Fn {(a: number): void; }function fn(a) {}let a: Fn = fn在這個例子里,TS無法推斷出函數(shù)fn的參數(shù)a是number類型,因?yàn)橘x值操作提供的類型上下文在右手端,而fn這個函數(shù)聲明的位置,并不是右手端。對應(yīng)的右手端位置并不是函數(shù)聲明,而是函數(shù)聲明的引用。所以TS無法靜態(tài)分析出fn中a的類型。
還有一個原因是fn的使用并不唯一,在這里我們將fn賦值給Fn類型的變量a,我們完全可以將它再賦值給Fn1類型的變量b,所以這種情況下fn中a的類型是由運(yùn)行時決定的,無法靜態(tài)分析出來。
這里Fn1要求入?yún)的類型是string,所以fn的入?yún)既可能是number也可能是string,只有運(yùn)行這個函數(shù)的時候才能確定知道參數(shù)a是什么類型。所以在這個例子中a會自動獲得隱式類型any。
進(jìn)行如下修改:
interface Fn {(a: number): void; }let a: Fn = function fn(a) {}在這里fn是一個表達(dá)式,并不會再當(dāng)前環(huán)境中新建一個名字fn,換句話說這個fn不會再用在別的地方,并且a這個名字直接出現(xiàn)在了右手端對應(yīng)位置,所以這個fn函數(shù)可以得到類型上下文Fn,從而推斷出參數(shù)a的類型是number。
總結(jié)
以上是生活随笔為你收集整理的TypeScript类型推论(Type Inference)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TypeScript 交叉类型(inte
- 下一篇: Chrome不显示OPTIONS请求的解