【Typescript】進擊的基礎(一)交叉類型和聯合類型-集合論角度理解

引言

  1. 是否思考過交叉類型中「交叉」一詞究竟表明什麼?爲何叫交叉類型?
  2. 參考如下示例:
interface A {
  x: number;
  y: number;
}
interface B {
  y: number;
  z: number;
}
type U = A & B;
type I = A | B;
複製代碼

具體使用屬性時,會發現ts的提示中,交叉類型 U 具備 x,y,z 三個屬性,而聯合類型 I 僅僅只有 y 屬性,這個「聯合」體如今哪裏?html

  1. 本文嘗試從集合角度(我的思考),理解其設計的原則和模式

正文

類型和集合

這裏須要重拾一下什麼是「類型」。拋開「實例化」這些術語,從集合角度出發,類型應該是指 具備特定特性的若干值的集合git

如在Typescript中,number類型是全部數字的集合,string類型是全部字符串的集合,字面量類型 1 是隻 包含一個元素 1 的集合,字面量類型 "1" 是隻包含一個元素 "1" 的集合。github

可是在對象中,集合的概念就很是容易混淆了,我之前一直不能理解爲何說 子類是父類的子集 ,明明子類會比父類擁有更多的屬性和方法,難道不是父類是子類的子集?typescript

這裏是我一直以來都陷入的誤區。看下面例子:安全

interface A {
  x: number;
}
interface B extends A {
  y: number;
}
const obj = {
  x: 1,
  xx: 2,
  xxx: 3,
};
const a: A = obj;
複製代碼

顯然,變量 aA 類型,可是實際上它所表明的對象,具備 x,xx,xxx 三個屬性。顯然,這裏能夠得出一個重要觀點:對象類型只要求它所描述的對象 這些屬性,而不必定是 有且僅有 這些屬性。函數

在這個基礎上,不難理解類型 B 是類型 A 的子集,由於 A 是全部具備 x 屬性的對象的集合(有就行,對象可能有其餘任意屬性),而 B 除了要求對象有 x 屬性,還要有一個 y 屬性,要求明顯比 A 更嚴格,套入集合的觀點,明顯 BA 的子集。ui

對象類型是若干對象的集合,而不是屬性的集合。只有一個對象具備它所描述的所有屬性,那麼該對象就是該類型的一員spa

:Typescript並不徹底基於集合的數學定義。爲了實際的類型安全,若是改爲用字面量對象賦值,將會報錯翻譯

const a: A = {
  x: 1,
  xx: 2, // ERROR
  xxx: 3, // ERROR
};
複製代碼

交集類型和並集類型運算符的簡單運算

交叉類型即 Intersection types ,從當前文章角度,翻譯它爲 交集類型 可能會更加合適。設計

聯合類型即 Union types,從當前文章角度,翻譯它爲 並集類型 可能會更加合適。

從集合角度,&,| 運算符的各類運算結果:

type A = 1 | 2 | 3;
type B = 2 | 3 | 4;
type C = A & B; // C爲: 2 | 3
type D = A & number; // D爲: 1 | 2 | 3
type E = A | number; // E爲: number
type F = number & string; // F爲: never
複製代碼

類型 A-E 套一下集合運算便可。至於類型 F , 雖然官方對 never 的討論定義只是 「不會出現的類型」 ,但我以爲實際上是 「空集」 的思想在潛移默化的影響他們,把 never 看做是空集就完事了。

至於官方給到的幾個運算特性,估計也是集合的思想在影響着,這些運算特性是顯然易見的:

對於交集運算符 &

  1. 惟一性: A & A 等價於 A.
  2. 知足交換律: A & B 等價於 B & A (函數調用和構造函數有些許不一樣,下面會講).
  3. 知足結合律: (A & B) & C 等價於 A & (B & C).
  4. 父類型收斂: 當且僅當 BA 的父類型時,A & B 等價於 A.

對於並集運算符 |

  1. 惟一性: A | A 等價於 A.
  2. 知足交換律: A | B 等價於 B | A.
  3. 知足結合律: (A | B) | C 等價於 A | (B & C).
  4. 子類型收斂: 當且僅當 BA 的子類型時,A | B 等價於 A.

:Typescript的用詞是 Supertype 和 Subtype,而不是 Superclass 和 Subclass 。也就是 AB 並不必定是 Class 也多是typeinterface,相信你能理解這些名詞的差別。

交集類型和並集類型運算符的高級運算

Intersection types 交集類型:

  • &| 有更高的運算優先級
  • 若是類型 A 的屬性 p 是類型 X ,類型 B 的屬性 p 是類型 Y ,那麼交集類型 A & B 中對應的屬性 pX & Y
  • 交集類型 A & B 具備類型 AB 的全部屬性(即 屬性的並集 )。再次強調,交集類型 不是屬性的交集,而是 對象的交集。(類型的屬性越多,表明限制條件越多,對象只有知足這些條件的並集,才能歸爲該交集類型)
  • 若是 AB 是函數類型,那麼 A & B 表明重載函數,此時集合運算順序即爲重載函數的函數簽名順序(如vscode智能提示中,第一個提示的重載函數類型會是 A 類型)
  • 若是 A 是對象類型,B 是原始數據類型(string,number 這些),那麼把原始數據類型 B 看做是對象類型,它們屬性的並集(好比會包含stringindexOf,substr 等方法),就是類型 A & B 具備的屬性。

最後兩點可能要結合實際代碼理解:

  1. 函數的交集類型是重載函數:
type M = (a: number) => number;
type N = (a: string) => string;

function overload(a: number): number;
function overload(a: string): string;
function overload(a: number | string) {
  return a;
}

let m: M = overload; // OK
let n: N = overload; // OK
let mn: M & N = overload; // OK
複製代碼

顯然類型 M 描述的函數是:可以 接受一個 number 並返回 number。注意是 可以,而不是 能且只能,和前面的對象類型有殊途同歸之妙。即overload 知足類型 M 是集合 M 的一員,也知足類型 N 是集合 N 的一員,因此它必然是 M & N 這個交集中的元素。

所以最終表現形式會是:函數類型的交集類型是重載函數

  1. 對象類型和原始數據類型的交集是它們的屬性並集,不一樣的原始數據類型的交集是never(空集):

其實我也沒什麼特別好的數學解釋,畢竟他們也不是徹底按照嚴格的數學定義來實現的,還有考慮一些實際狀況,有興趣的到這裏圍觀:issues: Intersection types

這個是根據實際狀況定義的,看如下代碼:

let nObj: number & { a: number } = new Number(1) as any;
nObj.a = 2;
console.log(nObj.a); // 打印 2
console.log(++nObj); // 打印 2
console.log(nObj * 10); // 打印 20

let x = Object.assign(1, { a: 2 });
console.log(x.a); // 打印 2
console.log(++x); // 打印 2
console.log(x * 10); // 打印 20
複製代碼

很明顯,number & { a: number } 這種類型是有意義的。通過取捨,最終決定它是成立的,被添加到TS當中。

string & number 不能像上面那樣玩,你很難想象 Object.assign(1, "1") 是個什麼東西,雖然它在js中的確存在。各類討論和後續迭代中,最終敲定原始數據類型的交集爲never

:最後這一點的確想不到怎麼用集合論來解釋,只能額外記憶爲 根據實際狀況特別定義 了。

Union types 並集類型:

  • 若是類型 A 的屬性 p 是類型 X ,類型 B 的屬性 p 是類型 Y ,那麼並集類型 A | B 中對應的屬性 pX | Y
  • 並集類型 A & B 必定具備 類型 AB 的重複屬性(即 屬性的交集 )。這裏也須要從實際出發,從類型安全角度,在不知道交集中的某個元素具體屬於 A 仍是 B 的狀況下,訪問時 TS只能給你提示它們必定都有的屬性,所以表現上會是交集;賦值 時很少說了,知足AB就行,標準的 並集 概念。
  • 函數類型一樣表現爲並集的概念,不須要特別理解。惟一值得注意的地方是,在TS確實沒法推測函數類型的狀況下,調用這個函數:逆變 位置(參數)取交集類型,協變 位置(返回值)取並集類型。

協變與逆變

後記

交叉類型的討論:github.com/microsoft/T…

交叉類型的PR:github.com/microsoft/T…

聯合類型的討論:github.com/microsoft/T…

聯合類型的PR:github.com/microsoft/T…

🤔還有個坑沒填,因此這個「交叉」是...?😂畢竟這裏是TS,不是數學領域,當術語吧

相關文章
相關標籤/搜索