成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

快速理解 TypeScript 的逆變和協變

開發 前端
TypeScript 給 JavaScript 添加了一套靜態類型系統,是為了保證類型安全的,也就是保證變量只能賦同類型的值,對象只能訪問它有的屬性、方法。

深入學習 TypeScript 類型系統的話,逆變、協變、雙向協變、不變是繞不過去的概念。

這些概念看起來挺高大上的,其實并不復雜,這篇文章我們就來學習下它們吧。

類型安全和型變

TypeScript 給 JavaScript 添加了一套靜態類型系統,是為了保證類型安全的,也就是保證變量只能賦同類型的值,對象只能訪問它有的屬性、方法。

比如 number 類型的值不能賦值給 boolean 類型的變量,Date 類型的對象就不能調用 exec 方法。

這是類型檢查做的事情,遇到類型安全問題會在編譯時報錯。

但是這種類型安全的限制也不能太死板,有的時候需要一些變通,比如子類型是可以賦值給父類型的變量的,可以完全當成父類型來使用,也就是“型變”(類型改變)。

這種“型變”分為兩種,一種是子類型可以賦值給父類型,叫做協變,一種是父類型可以賦值給子類型,叫做逆變。

先來看下協變:

協變

其中協變是很好理解的,比如我們有兩個 interface:

interface Person {
name: string;
age: number;
}
interface Guang {
name: string;
age: number;
hobbies: string[]
}

這里 Guang 是 Person 的子類型,更具體,那么 Guang 類型的變量就可以賦值給 Person 類型:

這并不會報錯,雖然這倆類型不一樣,但是依然是類型安全的。

這種子類型可以賦值給父類型的情況就叫做協變。

為什么要支持協變很容易理解:類型系統支持了父子類型,那如果子類型還不能賦值給父類型,還叫父子類型么?

所以型變是實現類型父子關系必須的,它在保證類型安全的基礎上,增加了類型系統的靈活性。

逆變相對難理解一些:

逆變

我們有這樣兩個函數:

let printHobbies: (guang: Guang) => void;
printHobbies = (guang) => {
console.log(guang.hobbies);
}
let printName: (person: Person) => void;
printName = (person) => {
console.log(person.name);
}

printHobbies 的參數是 printName 參數的子類型。

那么問題來了,printName 能賦值給 printHobbies 么?printHobbies 能賦值給 printName 么?

測試一下發現是這樣的:

printName 的參數不是 printHobbies 的父類型么,為啥能賦值給子類型?

因為這個函數調用的時候是按照 Guang 來約束的類型,但實際上函數只用到了父類型 Person 的屬性和方法,當然不會有問題,依然是類型安全的。

這就是逆變,函數的參數有逆變的性質(而返回值是協變的,也就是子類型可以賦值給父類型)。

那反過來呢,如果 printHoobies 賦值給 printName 會發生什么?

因為函數聲明的時候是按照 Person 來約束類型,但是調用的時候是按照 Guang 的類型來訪問的屬性和方法,那自然類型不安全了,所以就會報錯。

但是在 ts2.x 之前支持這種賦值,也就是父類型可以賦值給子類型,子類型可以賦值給父類型,既逆變又協變,叫做“雙向協變”。

但是這明顯是有問題的,不能保證類型安全,所以之后 ts 加了一個編譯選項 strictFunctionTypes,設置為 true 就只支持函數參數的逆變,設置為 false 則是雙向協變。

我們把 strictFunctionTypes 關掉之后,就會發現兩種賦值都可以了:

這樣就支持函數參數的雙向協變,類型檢查不會報錯,但不能嚴格保證類型安全。

開啟之后,函數參數就只支持逆變,子類型賦值給父類型就會報錯:

在類型編程中這種逆變性質有什么用呢?

還記得之前聯合轉交叉的實現么?

type UnionToIntersection<U> = 
(U extends U ? (x: U) => unknown : never) extends (x: infer R) => unknown
? R
: never

類型參數 U 是要轉換的聯合類型。

U extends U 是為了觸發聯合類型的 distributive 的性質,讓每個類型單獨傳入做計算,最后合并。

利用 U 做為參數構造個函數,通過模式匹配取參數的類型。

結果就是交叉類型:

我們通過構造了多個函數類型,然后模式提取參數類型的方式,來實現了聯合轉交叉,這里就是因為函數參數是逆變的,會返回聯合類型的幾個類型的子類型,也就是更具體的交叉類型。

逆變和協變都是型變,是針對父子類型而言的,非父子類型自然就不會型變,也就是不變:

不變

非父子類型之間不會發生型變,只要類型不一樣就會報錯:

那類型之間的父子關系是怎么確定的呢,好像也沒有看到 extends 的繼承?

類型父子關系的判斷

像 java 里面的類型都是通過 extends 繼承的,如果 A extends B,那 A 就是 B 的子類型。這種叫做名義類型系統(nominal type)。

而 ts 里不看這個,只要結構上是一致的,那么就可以確定父子關系,這種叫做結構類型系統(structual type)。

還是拿上面那個例子來說:

Guang 和 Person 有 extends 的關系么?

沒有呀。

那是怎么確定父子關系的?

通過結構,更具體的那個是子類型。這里的 Guang 有 Person 的所有屬性,并且還多了一些屬性,所以 Guang 是 Person 的子類型。

注意,這里用的是更具體,而不是更多。

判斷聯合類型父子關系的時候, 'a' | 'b' 和 'a' | 'b' | 'c' 哪個更具體?

'a' | 'b' 更具體,所以 'a' | 'b' 是 'a' | 'b' | 'c' 的子類型。

測試下:

總結

ts 通過給 js 添加了靜態類型系統來保證了類型安全,大多數情況下不同類型之間是不能賦值的,但是為了增加類型系統靈活性,設計了父子類型的概念。父子類型之間自然應該能賦值,也就是會發生型變。

型變分為逆變和協變。協變很容易理解,就是子類型賦值給父類型。逆變主要是函數賦值的時候函數參數的性質,參數的父類型可以賦值給子類型,這是因為按照子類型來聲明的參數,訪問父類型的屬性和方法自然沒問題,依然是類型安全的。但反過來就不一定了。

不過 ts 2.x 之前反過來依然是可以賦值的,也就是既逆變又協變,叫做雙向協變。

為了更嚴格的保證類型安全,ts 添加了 strictFunctionTypes 的編譯選項,開啟以后函數參數就只支持逆變,否則支持雙向協變。

型變都是針對父子類型來說的,非父子類型自然就不會型變也就是不變。

ts 中父子類型的判定是按照結構來看的,更具體的那個是子類型。

理解了如何判斷父子類型(結構類型系統),父子類型的型變(逆變、協變、雙向協變),很多類型兼容問題就能得到解釋了。

責任編輯:姜華 來源: 神光的編程秘籍
相關推薦

2020-09-29 06:37:30

Java泛型

2009-08-03 18:24:28

C# 4.0協變和逆變

2009-05-27 11:30:20

C#Visual Stud協變

2020-08-03 08:13:51

Vue3TypeScript

2011-01-14 10:27:18

C#.netasp.net

2012-03-13 09:32:15

C#協變

2013-10-31 09:36:43

程序員程序高手

2010-08-17 11:18:19

BISAP商業智能

2010-01-20 09:17:46

2015-02-06 17:00:04

2018-04-27 16:45:41

華為

2018-08-01 15:48:05

搜狗

2023-01-29 09:15:42

2011-11-18 10:00:05

云計算IT管理

2011-11-17 17:22:10

IT管理云計算

2015-09-11 10:45:55

服務器華為

2020-12-25 16:52:28

CIO數字化轉型計世

2009-07-07 11:04:12

百變蠕蟲病毒卡巴斯基
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲精品一区二区网址 | 黄色大片在线免费观看 | 国产一区二区免费电影 | 久久久天天 | 在线中文字幕第一页 | 久久99精品久久久久蜜桃tv | 精品少妇一区二区三区在线播放 | 中文字幕日韩三级 | 一区二区av| 五月婷婷 六月丁香 | 成人免费在线电影 | 国产欧美一区二区三区日本久久久 | 色精品| 在线欧美亚洲 | 色综合天天天天做夜夜夜夜做 | 亚洲综合99 | 免费成人高清 | 97精品久久 | 久久男人天堂 | 成人免费激情视频 | 女同久久另类99精品国产 | 一区二区国产精品 | 99九九久久 | 麻豆精品国产91久久久久久 | 黄色大片免费网站 | 精品久久久久久久久久久 | 美女福利视频 | 欧美一区二区三区四区在线 | 欧美一级二级视频 | 91在线免费视频 | 国产午夜精品一区二区三区四区 | 亚洲黄色av网站 | 视频一区二区在线观看 | 欧美亚洲国产一区 | 国产av毛片 | 日韩综合 | 九色www| 成人性视频免费网站 | 一区二区av | 中文字幕一区二区三区在线观看 | 成人免费一区二区三区牛牛 |