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