從兩個(gè)角度看 Typescript 中的類型是什么?

0. 作者以及原文介紹
作者是 Dr. Axel Rauschmayer,號(hào)稱”德國(guó)阮一峰“,本文原文來(lái)自于他的博客:https://2ality.com/2020/02/understanding-types-typescript.html,不熟悉他的可以關(guān)注一下他的博客。
以下是原文:
1. 每個(gè)角度都從這三個(gè)問(wèn)題來(lái)解釋
以下三個(gè)問(wèn)題對(duì)于理解類型是如何工作的非常重要,需要從這兩個(gè)角度中的每一個(gè)角度來(lái)回答。
myVariable的類型MyType意味著什么?
let myVariable: MyType = /*...*/;
Sourcetype可以分配給TargetType嗎?
let source: SourceType = /*...*/;
let target: TargetType = source;
TypeUnion是如何從Type1、Type2和Type3衍生而來(lái)的?
type TypeUnion = Type1 | Type2 | Type3;
2. 角度 1:類型是一組值
從這個(gè)角度來(lái)看,類型是一組值:
如果
myVariable具有MyType類型,這意味著可以分配給myVariable的所有值都必須是集合MyType的元素。如果
Sourcetype可以分配給TargetType,那么Sourcetype是TargetType的子集。因此,TargetType也允許SourceType所允許的所有值。類型
Type1、Type2和Type3的聯(lián)合類型是定義它們的集合在集合論中的并集。
3. 角度 2:類型兼容關(guān)系
從這個(gè)角度來(lái)看,我們不關(guān)心值以及它們?cè)趫?zhí)行代碼時(shí)如何流動(dòng)。相反,我們采取了一種更為靜態(tài)的觀點(diǎn):
- 源代碼有個(gè)位置,每個(gè)位置都有一個(gè)靜態(tài)類型。在支持 Typescript 的編輯器中,如果我們將鼠標(biāo)懸停在某個(gè)位置的上方,就可以看到該位置的靜態(tài)類型。
- 當(dāng)源位置通過(guò)賦值、函數(shù)調(diào)用等方式連接到目標(biāo)位置時(shí),源位置的類型必須與目標(biāo)位置的類型兼容。Typescript 規(guī)范通過(guò)所謂的類型關(guān)系定義類型的兼容性。
- 類型關(guān)系分配兼容性定義了源類型
S何時(shí)可以分配給目標(biāo)類型T:S和T都是一樣的類型S或者T是 any 類型。- 等等
讓我們考慮以下問(wèn)題:
- 如果
myVariable的靜態(tài)類型可以分配給MyType,那么myVariable就具有類型MyType - 如果
SourceType和TargetType是互相兼容的,那么SourceType可以分配給TargetType - 聯(lián)合類型的工作方式是通過(guò)類型關(guān)系成員定義的。
類型系統(tǒng)一個(gè)有趣的特點(diǎn)是,同一個(gè)變量在不同的位置可以有不同的靜態(tài)類型:
const arr = [];
// %inferred-type: any[]
arr;
arr.push(123);
// %inferred-type: number[]
arr;
arr.push('abc');
// %inferred-type: (string | number)[]
arr;
4. 標(biāo)準(zhǔn)類型系統(tǒng)和結(jié)構(gòu)類型系統(tǒng)
靜態(tài)類型系統(tǒng)的職責(zé)之一是確定兩個(gè)靜態(tài)類型是否兼容:
- 實(shí)際參數(shù)的靜態(tài)類型 U(例如,通過(guò)函數(shù)調(diào)用提供)
- 對(duì)應(yīng)形式參數(shù)的靜態(tài)類型 T(指定為函數(shù)定義的一部分)
這通常意味著要檢查 U 是否是 T 的子類型。這種檢查的兩種方法(大致)是:
在標(biāo)準(zhǔn)類型中,如果兩個(gè)靜態(tài)類型具有相同的標(biāo)識(shí)(“名稱”) ,則它們是相等的。一種類型是另一種類型的子類型,它們的子類型關(guān)系是顯式聲明的。
具有標(biāo)準(zhǔn)類型的語(yǔ)言有 c++ 、 Java、 c# 、 Swift 和 Rust
在結(jié)構(gòu)類型系統(tǒng)中,如果兩個(gè)靜態(tài)類型具有相同的結(jié)構(gòu)(如果它們的部分具有相同的名稱和相同的類型) ,則它們是相等的。如果 U 包含 T 的所有部分(可能還包括其他部分) ,并且 U 的每個(gè)部分都包含 T 的相應(yīng)部分的子類型,那么一種類型 U 就是另一種類型 T 的子類型。
具有結(jié)構(gòu)類型的語(yǔ)言有 ocaml/reasonml、 Haskell 和 TypeScript
下面的代碼在標(biāo)準(zhǔn)類型系統(tǒng)中產(chǎn)生類型錯(cuò)誤(第 A 行) ,但在 Typescript 的結(jié)構(gòu)類型系統(tǒng)中是合法的,因?yàn)轭?A 和類 B 具有相同的結(jié)構(gòu):
class A {
name = 'A';
}
class B {
name = 'B';
}
const someVariable: A = new B(); // (A)
Typescript 的接口在結(jié)構(gòu)上也能工作——它們不需要實(shí)現(xiàn)來(lái)匹配:
interface Point {
x: number;
y: number;
}
const point: Point = {x: 1, y: 2}; // OK
5. 進(jìn)一步閱讀
- Chapter “Type Compatibility” in the TypeScript Handbook[1]
- Section “TypeRelationships” in the TypeScript Specification[2]
如果翻譯得不對(duì)的地方希望您可以幫忙指出來(lái)。
參考資料
[1]Chapter “Type Compatibility” in the TypeScript Handbook: https://www.typescriptlang.org/docs/handbook/type-compatibility.html
[2]Section “TypeRelationships” in the TypeScript Specification: https://github.com/microsoft/TypeScript/blob/master/doc/spec.md#311-type-relationships
推薦閱讀
我的公眾號(hào)能帶來(lái)什么價(jià)值?(文末有送書規(guī)則,一定要看)
每個(gè)前端工程師都應(yīng)該了解的圖片知識(shí)(長(zhǎng)文建議收藏)
為什么現(xiàn)在面試總是面試造火箭?
