詳解Typescript中奇怪的賦值操作
前言
本文主要討論在typescript中一些奇怪的賦值語句,探索其背后原因,更深入的了解typescript作為一個(gè)結(jié)構(gòu)化系統(tǒng)的特性。
我們先看這樣一個(gè)例子:
// 定義一個(gè)Cat類 class Cat { name!: string age!: number ? constructor(name: string, age: number) { this.name = name this.age = age } } // 定義一個(gè)Dog接口類型 interface Dog { name: string age: number } ? let dog: Dog = { name: 'wuyue', age: 3 } ? let cat: Cat cat = new Cat('qiqi', 2) cat = dog // is ok dog = cat // is ok cat = { name: 'jack', age: 12 } // is ok
- 在上面的例子中,我們定義了一個(gè)Cat類,一個(gè)Dog接口類型, Cat類的實(shí)例對(duì)象與Dog類型的變量相互賦值,可以通過Typescript的類型檢查,而將
{ name: 'jack', age: 12 }
分配給一個(gè)Cat類型的變量也是可以的。 - 這是結(jié)構(gòu)化類型系統(tǒng)的特性,Typescript就采用了這種結(jié)構(gòu)類型。結(jié)構(gòu)化類型系統(tǒng)的思想在于,名稱不重要,重要的是它們是否具有類型的相同成員,如果是則是兼容的。 結(jié)構(gòu)化類型系統(tǒng)也叫鴨子類型, 如果你看到一只鳥走起來像鴨子,游泳像鴨子,叫得也像鴨子,那么這只鳥就是鴨子。
- 上個(gè)例子中cat變量,dog變量,以及
{ name: 'jack', age: 12 }
字面量他們都具有相同的結(jié)構(gòu),所以Typescript在類型檢查時(shí)認(rèn)為他們之間的賦值操作是合法的。
我們?cè)賮砜戳硪粋€(gè)例子
// 定義一個(gè)Cat類 class Cat { name!: string age!: number ? constructor(name: string, age: number) { this.name = name this.age = age } } // 定義一個(gè)Dog接口類型 interface Dog { name: string age: number } let dog: Dog = { name: 'wuyue', age: 3 } ? function getCat(cat: Cat) {} ? getCat(new Cat('cc', 2)) // ok getCat(dog) // ok getCat({ name: 'jack', age: 12 }) // ok const obj = { name: 'jack', age: 12, sex: 1 } // ok getCat(obj) // ? ok
在這個(gè)例子中,我們定義的Cat類和Dog接口類型和上面的例子一致,緊接著我們定義了一個(gè)getCat的函數(shù),接受一個(gè)Cat類型的形參。
根據(jù)上一個(gè)例子中我們學(xué)到的結(jié)構(gòu)化類型系統(tǒng)的知識(shí),可以很快的判斷出來 new Cat('cc', 2)
、dog
、{ name: 'jack', age: 12 }
都可以分配給Cat類型的變量,那么最后一個(gè)可以嗎? 我們?cè)?typescript Playground中測(cè)試發(fā)現(xiàn)是可以的,typescript對(duì)上面的代碼都通過了類型檢查,但是你可能還有困惑,obj明顯多了一個(gè)sex屬性,而sex屬性在Cat類型中并不存在,Typescript為什么也對(duì)其通過了類型檢查?先放下這個(gè)疑問,我們?cè)诳催@么一行代碼
getCat({ name: 'jack', age: 12, sex: 1 })
這行代碼竟然報(bào)錯(cuò)了,事情變得更加奇怪了。
再來看一個(gè)例子:
是不是很奇怪,同樣的值,直接賦值/傳參會(huì)報(bào)錯(cuò),如果先定義一個(gè)變量然后將這個(gè)變量進(jìn)行賦值/傳參就不會(huì)報(bào)錯(cuò)。
通過查閱各種官方資料發(fā)現(xiàn)這么一個(gè)結(jié)論: 在typescript類型系統(tǒng)中,對(duì)對(duì)象進(jìn)行類型檢查時(shí)定義的方式會(huì)對(duì)結(jié)果產(chǎn)生影響,在創(chuàng)建一個(gè)對(duì)象字面量并直接分配給具有某個(gè)類型的變量時(shí),typescript會(huì)最嚴(yán)謹(jǐn)?shù)尿?yàn)證對(duì)象, 進(jìn)行嚴(yán)格的屬性檢查(ECP),而當(dāng)將對(duì)象變量分配給另一個(gè)變量,對(duì)這個(gè)變量進(jìn)行類型檢查時(shí),typescript會(huì)進(jìn)行兼容性判斷,如果兼容則通過檢查,如果不兼容則報(bào)錯(cuò)。
而這個(gè)兼容性判斷是如何判斷的呢? 請(qǐng)記住下面這句話:
假如x要分配給y,在TypeScript中,X需要更具體,即X要有和Y相同的屬性或者更多。 可分配性指的就是兼容性。
如: let v = { name: 'john'; age: 20 }
兼容{ name: string }
, 類型兼容,通過檢查。let v = {foo: 1, bar: 2}
兼容{foo: number}
, 類型兼容,通過檢查。而這個(gè)問題可以被看成是類型加寬。
對(duì)于上面的現(xiàn)象,有好處也有壞處,對(duì)此有人提出來typescript新加入一個(gè)精確類型的語法,引起了激烈的討論:可以看下面這個(gè)鏈接。 #12936
結(jié)尾
在網(wǎng)上大多數(shù)的案例中,總是會(huì)出現(xiàn)這樣的例子:
interface Person { name: string age: number } let p: Person = { name: 'jack', age: 12, }
我們似乎認(rèn)為p變量一定要精確的滿足于Person的定義,如果多加了屬性那么就會(huì)報(bào)錯(cuò),這是typescript的基石,ts就應(yīng)該做這樣的事,其實(shí)不然,typescript作為一個(gè)結(jié)構(gòu)化系統(tǒng),多加了屬性并不會(huì)報(bào)錯(cuò), “多加了屬性那么就會(huì)報(bào)錯(cuò)” 的功能其實(shí)是在typescript中是一種linter校驗(yàn)的功能,而且它僅適用于對(duì)象字面量。所以有時(shí)候我們也會(huì)說Typescript是Javascript一個(gè)超級(jí)強(qiáng)大的linter工具。
到此這篇關(guān)于詳解Typescript中奇怪的賦值操作的文章就介紹到這了,更多相關(guān)Typescript賦值操作內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript 包裹節(jié)點(diǎn) 提高效率
模仿jQuery,創(chuàng)建幾個(gè)包裹節(jié)點(diǎn)的方法,發(fā)現(xiàn)jQuery的方法很低效啊,下一次他又可以說這幾個(gè)方法可以提升了多少多少了。2010-02-02使用JS判斷是否數(shù)字和小數(shù)點(diǎn)組合的數(shù)字的兩中方法比較(isNaN和逐判斷)
使用js判斷數(shù)字和小數(shù)點(diǎn)的方法非常之多。但是就目前而言,我見過最好用的判斷方法應(yīng)該來說是isNaN,它比較方便,而逐個(gè)比較的方法有一定的弊端。2009-09-09論壇轉(zhuǎn)貼工具中用到的正則表達(dá)式學(xué)習(xí)正則的好例子
論壇轉(zhuǎn)貼工具中用到的正則表達(dá)式學(xué)習(xí)正則的好例子...2007-11-11