TypeScript中交叉類型和聯(lián)合類型的區(qū)別詳解
1. 定義
1.1. 聯(lián)合類型(|)
在TS中,聯(lián)合類型表示:一個值可以是多種類型之一,使用邏輯“或”( | )運算符來分隔多個類型。
一個聯(lián)合類型的變量,在使用時可以是多個類型中的任意一種。
type UnionTypes = Type1 | Type2 | Type3;
1.2. 交叉類型(&)
在TS中,交叉類型表示:同時具備多種類型的值,使用邏輯“與”( & )運算符進行組合。
一個交叉類型的變量,將同時擁有多個類型的屬性和方法。
type IntersectionTypes = Type1 & Type2 & Type3;
2. 聯(lián)合類型
2.1. 基礎聯(lián)合類型
當一個變量可以是多種不同的類型時,可以使用聯(lián)合類型來定義它。
例如,一個變量可以是 string 類型或者 number 類型。
let data: string | number; data = 'hello ts'; data = 123; data = false; // 編譯錯誤:不能將類型“boolean”分配給類型“string | number”。ts(2322)
上面這段代碼中,我們定義了一個變量 data,類型為 number 和 string 的聯(lián)合類型,因此,data 的值只能是這兩種類型中的其中一種,復制其它類型的值會報錯。
2.2. 對象聯(lián)合類型
對象聯(lián)合類型只能訪問聯(lián)合中所有類型共有的成員。
interface Admin {
name: string;
age: number;
}
interface User {
name: string;
sayHi(): void;
}
declare function Employee(): Admin | User;
let employee = Employee();
employee.name = 'Echo';
// 下面語句會報錯:age屬性不是 Admin 和 User 共有的屬性
employee.age = 26; // 編譯錯誤:類型“Admin | User”上不存在屬性“age”。類型“User”上不存在屬性“age”。ts(2339)上面這段代碼中,定義了兩個接口 Admin 和 User,接著使用 declare function 聲明了一個 Employee 函數(shù),該函數(shù)的返回類型為 Admin 或 User。之后通過調(diào)用 Employee 函數(shù)并將返回值賦給了 employee 變量。接著將 employee 對象中 name 屬性的值設置為 'Echo' 是可以的,因為 name 屬性是 Admin 和 User 共有的屬性。而將 employee 對象中 age 屬性的值設置為 26 時會出現(xiàn)編譯錯誤,錯誤信息指出類型 Admin | User 上不存在屬性 age。這是因為 age 屬性只存在于 Admin 接口中,而不屬于 User 接口。
造成該錯誤的原因是,TypeScript 在聯(lián)合類型上只能訪問聯(lián)合類型中所有類型的共有屬性和方法。 因此,通過聯(lián)合類型的變量只能訪問 name 屬性,而不能訪問 age 屬性。
2.3. 字面量聯(lián)合類型
聯(lián)合類型可以與字面量類型一起使用,用于限定一個值只能是某幾個特定的值之一。
let direction: "Up" | "Right" | "Down" | "Left"; direction = "Right"; direction = "none"; // 編譯錯誤,只能取值為 "Up" | "Right" | "Down" | "Left"
3. 交叉類型
在 TypeScript 中,交叉類型(Intersection Types)允許我們將多個類型合并為一個新的類型。
使用交叉類型可以將多個對象的屬性和方法合并到一個新的對象中。
type Person = {
name: string;
}
type User = {
age: number;
}
let person: Person & User;
person = {
name: 'Echo',
age: 26,
}
// 編譯錯誤:
// 不能將類型“{ name: string; }”分配給類型“Person & User”。
// 類型 "{ name: string; }" 中缺少屬性 "age",但類型 "User" 中需要該屬性。ts(2322)
// index.ts(7, 3): 在此處聲明了 "age"。
person = {
name: 'Steven',
}上面這段代碼中,我們定義了 Person 和 User 兩個類型,然后,我們定義一個變量 person,它的類型是使用交叉類型 Person & User 來創(chuàng)建的一個新類型,那么,此時變量 person 就同時具備了 name 和 age 屬性。
3.1. 交叉類型的成員類型是基礎類型
交叉類型的成員類型可以為任意類型,但需要注意的是,如果交叉類型的成員類型是基礎類型時,交叉類型的結(jié)果是 never。
type T1 = string & number; // 等同于 type T1 = never type T2 = number & boolean; // 等同于 type T2 = never
3.2. 交叉類型的成員類型是對象類型
當交叉類型的成員類型為對象類型時,結(jié)果類型又會是什么?
下面我們看一個簡單的例子:
type TypeA = {
x: number;
y: number;
}
type TypeB = {
y: number;
z: number;
}
type TypeC = {
z: number;
}上面這段代碼中,我們定義了三個類型:TypeA、TypeB 和 TypeC,分別表示類型 A、B 和 C,類型 A 具有屬性成員 x 和 y,類型 B 具有屬性成員 y 和 z,類型 C 具有屬性成員 z,每個類型具有不同的屬性成員。
type MergedType = TypeA & TypeB & TypeC;
上面這段代碼中,我們使用交叉類型 TypeA & TypeB & TypeC 創(chuàng)建了一個新的類型 MergedType,它包含了類型 A、B 和 C 的屬性成員,那么,合并后的交叉類型的成員類型為:屬性成員 x 的類型是 A 的類型,屬性成員 y 的類型是 A 和 B 的交叉類型,屬性成員 z 的類型是 B 和 C 的交叉類型。
let t: MergedType;
const t1 = {
x: 1,
y: 2,
z: 3,
}
const t2 = {
x: 10,
y: 20,
}
t = t1;
// 編譯錯誤:
// 不能將類型“{ x: number; y: number; }”分配給類型“MergedType”。
// 類型 "{ x: number; y: number; }" 中缺少屬性 "z",但類型 "TypeB" 中需要該屬性。ts(2322)
// index.ts(10, 3): 在此處聲明了 "z"。
t = t2;上面這段代碼中,定義了一個變量 t,它的類型是 TypeA & TypeB & TypeC 組成的交叉類型,然后再定義了兩個變量 t1 和 t2,t1 同時滿足 TypeA、TypeB 和 TypeC 類型約束,因此能賦值給交叉類型 t。而 t2 滿足 TypeA 類型約束,是 TypeA 類型,但并不能賦值給交叉類型 t,當 t2 賦值給 t 的時候,編譯器會報錯。
由此可見:交叉類型的類型成員由各個類型成員的屬性成員的并集組成,并且這些屬性成員的類型是各個成員類型的交叉類型。這種規(guī)則使得交叉類型能夠?qū)⒍鄠€類型的屬性成員合并到一個類型中,并且可以同時訪問這些屬性成員。
3.3. 成員類型合并
如果交叉類型的成員類型中有相同的類型,合并后的交叉類型將只保留一份該成員的類型。
type T1 = string & string; // 等同于 type T1 = string type T2 = string & string & string; // 等同于 type T2 = string
上面這段代碼中,類型 T1 由兩個 string 構(gòu)成,由于成員類型相同,所以合并成為一個 string。類型 T2 由三個 string 構(gòu)成,由于成員類型相同,所以合并成為一個 string。
3.4. 交叉類型的索引簽名
當交叉類型的成員類型之一具有數(shù)字索引簽名(即可通過數(shù)字索引訪問)或字符串索引簽名(即可通過字符串索引訪問)時,結(jié)果類型也將包含相應的數(shù)字索引簽名或字符串索引簽名。
結(jié)果類型的索引簽名值類型是各個成員類型索引簽名值類型的交叉類型。也就是說,通過交叉類型合并的結(jié)果類型的索引簽名值類型將是各個成員類型索引簽名值類型的交叉類型。
type TypeA = {
[key: string]: string;
};
type TypeB = {
[key: number]: string;
};
type MergedType = TypeA & TypeB;
const mergedObject: MergedType = {
name: 'Echo',
gender: 'Male',
city: 'Guang Zhou',
1: 'abcd',
};
console.log(mergedObject['name']); // 輸出:Echo
console.log(mergedObject['gender']); // 輸出:Male
console.log(mergedObject['city']); // 輸出:Guang Zhou
console.log(mergedObject[1]); // 輸出:abcd上面這段代碼中,定義了兩個類型 TypeA 和 TypeB,其中,TypeA 具有字符串索引簽名,TypeB 具有數(shù)字索引簽名,也就是說,TypeA 允許使用字符串作為索引,而 TypeB 允許使用數(shù)字作為索引。然后,使用交叉類型 TypeA & TypeB 創(chuàng)建了一個新的類型 MergedType,它包含了 TypeA 和 TypeB 的索引簽名。接著,我們創(chuàng)建了一個名為 mergedObject 的對象,它的類型指定為交叉類型 MergedType,該對象可以通過數(shù)字索引或字符串索引來訪問,并給這些索引賦予了相應的值。最后,我們通過索引訪問 mergedObject 對象的值來驗證交叉類型的索引簽名的合并情況。
3.5. 交叉類型的調(diào)用簽名
當交叉類型的成員類型中至少有一個具有調(diào)用簽名時,交叉類型的結(jié)果類型也會包含這個調(diào)用簽名。
換句話說,交叉類型中至少一個成員的調(diào)用簽名會被合并到結(jié)果類型中。
此外,如果交叉類型的多個成員類型都有調(diào)用簽名,那么結(jié)果類型將會形成調(diào)用簽名重載的結(jié)構(gòu)。調(diào)用簽名重載允許我們?yōu)橥粋€函數(shù)提供多個不同的調(diào)用方式,具體取決于參數(shù)類型和返回值類型。
可以將交叉類型的成員類型的調(diào)用簽名視為函數(shù)的簽名,交叉類型的結(jié)果類型即為這些簽名的合并。
type FunctionA = (x: number, y: number) => number;
type FunctionB = (x: string, y: string) => string;
type FunctionType = FunctionA & FunctionB;
const option: FunctionType = (x, y) => x + y;
console.log(option(10, 20)); // 輸出: 30
console.log(option('a', 'b')); // 輸出: ab上面這段代碼中,定義了兩個類型 FunctionA 和 FunctionB,它們接收 x 和 y 兩個參數(shù),其中,F(xiàn)unctionA 兩個參數(shù)的類型和函數(shù)返回值的類型都是 number 類型,F(xiàn)unctionB 兩個參數(shù)的類型和函數(shù)返回值的類型都是 string 類型。然后,使用交叉類型 FunctionA & FunctionB 創(chuàng)建了一個新的類型 FunctionType,這個交叉類型包含了兩個成員類型的調(diào)用簽名。最后,我們創(chuàng)建了一個名為 option 的變量,它的類型被定義為 FunctionType,也就是 FunctionA 和 FunctionB 的交叉類型。我們可以使用 option 等同于調(diào)用兩個函數(shù)的方式來執(zhí)行相應的運算,option(10, 20) 相當于加法運算,輸出結(jié)果為:30,option('a', 'b') 相當于字符串的拼接,輸出結(jié)果為:ab。
3.6. 交叉類型的構(gòu)造簽名
當交叉類型的成員類型中至少有一個具有構(gòu)造簽名時,交叉類型的結(jié)果類型也會包含這個構(gòu)造簽名。
換句話說,交叉類型中至少存在一個成員的構(gòu)造簽名會被合并到結(jié)果類型中。
如果交叉類型的多個成員類型都具有構(gòu)造簽名,那么結(jié)果類型將形成構(gòu)造簽名重載的結(jié)構(gòu)。構(gòu)造簽名重載允許我們?yōu)橥粋€類提供多個不同的構(gòu)造方式,具體取決于參數(shù)列表。
interface Foo {
new (name: string): string
}
interface Bar {
new (name: number): number;
}
type FooBar = Foo & Bar;
declare const T: FooBar;
const instance1 = new T('Echo');
const instance2 = new T(26);上面這段代碼中,我們定義了兩個接口 Foo 和 Bar,它們都具有構(gòu)造簽名,分別接受不同的參數(shù)類型并返回對應的類型。接著,我們使用交叉類型 Foo & Bar 創(chuàng)建了一個新的類型 FooBar,它是 Foo 和 Bar 的交叉類型,意味著 FooBar 同時具備了 Foo 和 Bar 接口的構(gòu)造簽名。然后,通過 declare 關(guān)鍵字聲明了一個常量 T,它的類型被定義為 FooBar。接著我們創(chuàng)建了兩個實例 instance1 和 instance2。由于 T 的類型為 FooBar,我們可以使用 new T 的語法來實例化對象。對于 instance1,使用字符串 'Echo' 作為參數(shù)傳遞給構(gòu)造函數(shù),這符合 Foo 接口中定義的構(gòu)造簽名,所以實例化成功,返回一個字符串類型的實例。對于 instance2,使用數(shù)字 26 作為參數(shù)傳遞給構(gòu)造函數(shù),這符合 Bar 接口中定義的構(gòu)造簽名,所以實例化成功,返回一個數(shù)字類型的實例。
4. 總結(jié)
- 聯(lián)合類型只能訪問共有的屬性和方法。例如,如果一個變量是 number 類型或者 string 類型,那么只能使用這兩種類型共有的屬性和方法。
- 聯(lián)合類型中的變量,如果在特定條件下可以判斷出其具體的類型,可以使用類型斷言(as語法)來告訴編譯器具體的類型。
- 交叉類型的結(jié)果包含了所有成員類型的屬性和方法,通過合并同名成員實現(xiàn)。屬性會合并為并集類型,方法會合并為聯(lián)合類型。
- 當成員類型具有調(diào)用簽名或構(gòu)造簽名時,交叉類型的結(jié)果將形成相應簽名的重載。
以上就是TypeScript中交叉類型和聯(lián)合類型的區(qū)別詳解的詳細內(nèi)容,更多關(guān)于TypeScript交叉類型和聯(lián)合類型區(qū)別的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript禁止右擊保存圖片,禁止拖拽圖片的實現(xiàn)代碼
這篇文章主要介紹了JavaScript禁止右擊保存圖片,禁止拖拽圖片的實現(xiàn)代碼,代碼簡單易懂,非常不錯,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-04-04
JavaScript基于activexobject連接遠程數(shù)據(jù)庫SQL Server 2014的方法
這篇文章主要介紹了JavaScript基于activexobject連接遠程數(shù)據(jù)庫SQL Server 2014的方法,結(jié)合實例形式分析了javascript使用activexobject遠程連接數(shù)據(jù)庫的相關(guān)操作技巧,需要的朋友可以參考下2017-07-07
微信小程序ajax實現(xiàn)請求服務器數(shù)據(jù)及模版遍歷數(shù)據(jù)功能示例
這篇文章主要介紹了微信小程序ajax實現(xiàn)請求服務器數(shù)據(jù)及模版遍歷數(shù)據(jù)功能,結(jié)合實例形式分析了微信小程序ajax調(diào)用及模板wx:for循環(huán)列表渲染相關(guān)操作技巧,需要的朋友可以參考下2017-12-12
JavaScript學習筆記之取數(shù)組中最大值和最小值
在實際業(yè)務中有的時候要取出數(shù)組中的最大值或最小值。但在數(shù)組中并沒有提供arr.max()和arr.min()這樣的方法。那么是不是可以通過別的方式實現(xiàn)類似這樣的方法呢?那么今天我們就來整理取出數(shù)組中最大值和最小值的一些方法,需要的朋友一起學習吧2016-03-03
JS中new?Date().Format("yyyy-MM-dd")?報錯的解決
這篇文章主要介紹了JS中new?Date().Format("yyyy-MM-dd")?報錯的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-01-01

