一文讀懂TS?中聯(lián)合類型和交叉類型各自的含義
聯(lián)合類型在 TypeScript 中相當(dāng)流行,你可能已經(jīng)用過很多次了。交叉類型稍微不那么常見。它們似乎引起更多的困惑。
你有沒有想過這些名字是怎么來的?雖然你可能對兩種類型的并集有一些直觀感受,但交集通常不太容易理解。
閱讀本文之后,你將對這些類型有更好的了解,這將使你在代碼中使用它們時更有信心。
一、簡單的聯(lián)合類型
聯(lián)合類型通常與 null
或 undefined
一起使用:
const sayHello = (name: string | undefined) => { /* ... */ };
例如,這里 name
的類型是 string | undefined
意味著可以將 string
或 undefined
的值傳遞給sayHello
函數(shù)。
sayHello("semlinker"); sayHello(undefined);
查看這個示例,你可以憑直覺知道類型 A 和類型 B 聯(lián)合后的類型是同時接受 A 和 B 值的類型。
二、對象類型的并集和交集
這種直覺也適用于復(fù)雜類型。
interface Foo { foo: string; name: string; } interface Bar { bar: string; name: string; } const sayHello = (obj: Foo | Bar) => { /* ... */ }; sayHello({ foo: "foo", name: "lolo" }); sayHello({ bar: "bar", name: "growth" });
Foo | Bar
是含有 Foo
或 Bar
所有必須屬性的類型。在 sayHello 內(nèi)部只能訪問 obj.name
,因?yàn)樗莾煞N類型都包含的唯一屬性。
那么 Foo
和 Bar
類型的交集又怎么樣?
const sayHello = (obj: Foo & Bar) => { /* ... */ }; sayHello({ foo: "foo", bar: "bar", name: "kakuqo" });
現(xiàn)在 sayHello
要求 obj 參數(shù)同時包含 foo
和 bar
的屬性。所以在 sayHello
內(nèi)部,有可能同時訪問obj.foo
,obj.bar
和 obj.name
。
嗯,但是它有什么交集呢?有人可能會說,因?yàn)?obj 同時具有 Foo 和 Bar 的屬性,所以它聽起來更像是屬性的并集,而不是交集。類似地,兩個對象類型聯(lián)合將得到一個類型,該類型只含有組成類型的屬性的交集。
三、文氏圖
文氏圖(英語:Venn diagram),或譯 Venn 圖、溫氏圖、維恩圖等,是在集合論(或者類的理論)數(shù)學(xué)分支中,在不太嚴(yán)格的意義下用以表示集合(或類)的一種草圖。它們用于展示在不同的事物群組(集合)之間的數(shù)學(xué)或邏輯聯(lián)系,尤其適合用來表示集合(或)類之間的 “大致關(guān)系”,它也常常被用來幫助推導(dǎo)(或理解推導(dǎo)過程)關(guān)于集合運(yùn)算(或類運(yùn)算)的一些規(guī)律。
在文氏圖法中,如果有論域,則以一個矩形框(的內(nèi)部區(qū)域)表示論域;各個集合(或類)就以圓/橢圓(的內(nèi)部區(qū)域)來表示。兩個圓/橢圓相交,其相交部分表示兩個集合(或類)的公共元素,兩個圓/橢圓不相交(相離或相切,而實(shí)際上在文氏圖中相切是沒有什么意義的,因?yàn)槲氖蠄D是以圖形的內(nèi)部區(qū)域來表示的)則說明這兩個集合(或類)沒有公共元素。
比如黃色的圓圈(集合 A)可以表示兩足的所有動物。藍(lán)色的圓圈(集合 B)可以表示會飛的所有動物。黃色和藍(lán)色的圓圈交疊的區(qū)域(叫做交集)包含會飛且兩足的所有動物 —— 比如鸚鵡。(把每個單獨(dú)的動物類型想像為在這個圖中的某個點(diǎn))。
集合 A 和 B 的組合區(qū)域叫做集合 A 和 B 的并集。在這個示例中并集包含要么兩足、要么會飛、要么兩足并且會飛的所有東西。圓圈交疊暗示著兩個集合的交集非空 —— 就是說在事實(shí)上有動物同時在黃色和藍(lán)色圓圈中。
需要注意的是,文氏圖與其它的圖示法一樣,它不能準(zhǔn)確表示一個集合(或類)中到底有哪些元素。
四、集合理論
你還記得數(shù)學(xué)課中稱為集合的概念嗎?在數(shù)學(xué)中,集合是對象(例如數(shù)字)的集合。例如 {1, 2, 7}
是一組。所有正數(shù)也可以形成一組(無限個)。
可以將集合合并在一起(并集)。{1, 2}
和 {4, 5}
的并集是 {1, 2, 4, 5}
。
集合也可以交叉。兩個集合的交集是一個集合,它只包含兩個集合中出現(xiàn)的那些數(shù)字。因此,{1, 2, 3}
和 {3, 4, 5}
的交集是 {3}
。
下面我們來換一種思考方式。假設(shè)有四個集合:紅色的東西,藍(lán)色的東西,大的東西,和小的東西。
如果你把所有紅色的東西和所有小的東西的集合相交,你就得到了屬性的并集 —— 集合里的所有東西都有紅色的屬性和小的屬性。
但如果取紅色小物體與藍(lán)色小物體的并集,則結(jié)果集中只有小(small)屬性是普遍存在的。“red small” 與 “blue small” 相交產(chǎn)生 “small”。
換句話說,取值域的并集會產(chǎn)生一組交叉的屬性,反之亦然。具體過程如下圖所示:
五、類型和集合之間的關(guān)系
計(jì)算機(jī)科學(xué)和數(shù)學(xué)在許多地方都有重疊。這樣的地方之一就是類型系統(tǒng)。
從數(shù)學(xué)角度看,一種類型是該類型所有可能值的集合。例如,string
類型是所有可能的字符串的集合:{'a', 'b', 'ab', ...}
。當(dāng)然,這是一個無限的集合。同樣,number
類型是一組所有可能的數(shù)字的集合:{1, 2, 3, 4, ...}
。
類型 undefined
是一個僅包含單個值的集合:{ undefined }
,該類型在 TypeScript 中被稱為單元類型。
那么對象類型(比如接口)呢?類型 Foo
是包含 foo 和 name 屬性的所有對象的集合。
六、了解聯(lián)合類型和交叉類型
有了這些知識,你現(xiàn)在就可以了解聯(lián)合和交叉類型的含義了。
聯(lián)合類型 A | B
表示一個集合,該集合是與類型A關(guān)聯(lián)的一組值和與類型 B 關(guān)聯(lián)的一組值的并集。交叉類型 A & B
表示一個集合,該集合是與類型 A 關(guān)聯(lián)的一組值和與類型 B 關(guān)聯(lián)的一組值的交集。
因此,Foo | Bar
表示有 foo 和 name 屬性的對象集和有 bar 和 name 屬性的對象集的并集。屬于這類集合的對象都含有 name 屬性。有些有 foo 屬性,有些有 bar 屬性。
而 Foo & Bar
表示具有 foo 和 name 屬性的對象集和具有 bar 和 name 屬性的對象集的交集。換句話說,集合包含了屬于由 Foo 和 Bar 表示的集合的對象。只有具有這三個屬性(foo、bar 和 name)的對象才屬于交集。
繼續(xù)閱讀:TypeScript 交叉類型
七、交叉類型的真實(shí)示例
聯(lián)合類型非常普遍,所以讓我們關(guān)注一個交叉類型的例子。
在 React 中,當(dāng)你聲明一個類組件時,可以使用它的屬性類型對其進(jìn)行參數(shù)化:
class Counter extends Component<CounterProps> { /* ... */ }
在類中,你可以通過 this.props 訪問屬性。然而, this.props
的類型不只是 CounterProps
,而是:
Readonly<CounterProps> & Readonly<{ children?: ReactNode; }>
這樣做的原因是 React 組件可以接收子元素:
<Counter><span>Hello Semlinker</span></Counter>
通過 children
屬性可以訪問到子元素。this.props
的類型反映了這一點(diǎn)。它是(readonly)CounterProps 和含有可選的 children 屬性的(readonly)對象類型交集。
在集合方面,它是含有 CounterProps 中定義的屬性的對象集和與含有可選 children 屬性的對象集的交集。結(jié)果是一組含有 CounterProps 所有屬性和可選 children 屬性的對象集。
八、總結(jié)
本文為了幫助讀者更好地理解 TypeScript 中的聯(lián)合類型和交叉類型,我們引入了文氏圖、集合理論及類型和集合之間的關(guān)系這些內(nèi)容。計(jì)算機(jī)科學(xué)和數(shù)學(xué)在許多地方都有重疊,理解數(shù)學(xué)相關(guān)的基本原理后可以使你更好地掌握編程概念。
九、參考資源
- wiki - 文氏圖
- The-meaning-of-union-and-interp-types
- naming-of-typescripts-union-and-interp-types
相關(guān)文章
JS當(dāng)前頁面登錄注冊框,固定DIV,底層陰影的實(shí)例代碼
下面小編就為大家?guī)硪黄狫S當(dāng)前頁面登錄注冊框,固定DIV,底層陰影的實(shí)例代碼。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-09-09JS實(shí)現(xiàn)獲取時間已經(jīng)時間與時間戳轉(zhuǎn)換
這篇文章主要為大家提供了用JavaScript編寫的獲取時間的類,以及時間戳轉(zhuǎn)時間的三種格式,文中的示例代碼講解詳細(xì),感興趣的可以了解一下2022-03-03為JS擴(kuò)展Array.prototype.indexOf引發(fā)的問題探討及解決
Array沒有indexOf方法,這樣在一個數(shù)組中查找某個元素的索引時比較麻煩,于是通過prototype原型擴(kuò)展了Array.prototype.indexOf(),在對數(shù)組進(jìn)行遍歷的時候卻出現(xiàn)了問題2013-04-04小程序rich-text組件如何改變內(nèi)部img圖片樣式的方法
這篇文章主要介紹了小程序rich-text組件如何改變內(nèi)部img圖片樣式的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-05-05分享bootstrap學(xué)習(xí)筆記心得(組件及其屬性)
Bootstrap是一種web框架,是基于HTML,CSS和JS的一種目前較為流行的前端框架。本篇文章將總結(jié)常用組件及其屬性,需要的朋友參考下吧2017-01-01