新聞中心
// 非對象類型交叉運(yùn)算
type N0 = string & number;
type N1 = any & 1;
type N2 = any & never;
// 對象類型交叉運(yùn)算
type A = { kind: 'a', foo: string };
type B = { kind: 'b', foo: number };
type C = { kind: 'c', foo: number };
type AB = A & B;
type BC = B & C;
// 函數(shù)類型交叉運(yùn)算
type F1 = (a: string, b: string) => void;
type F2 = (a: number, b: number) => void;
type Fn = F1 & F2
在學(xué)習(xí) TypeScript 的過程中,你可以把類型理解成一系列值的集合。比如,你可以把數(shù)字類型看作是所有數(shù)字的集合,1.0、68 就屬于這個集合中,而 "阿寶哥" 就不屬于這個集合,因?yàn)樗鼘儆谧址愋汀?/p>

新源網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)!從網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、響應(yīng)式網(wǎng)站等網(wǎng)站項(xiàng)目制作,到程序開發(fā),運(yùn)營維護(hù)。創(chuàng)新互聯(lián)自2013年創(chuàng)立以來到現(xiàn)在10年的時間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)。
同樣,對于對象類型來說,我們也可以把它理解成對象的集合。比如以上代碼中 Point 類型表示含有 x 和 y 屬性,且屬性值的類型都是 number 類型對象的集合。而 Named 類型表示含有 name 屬性且屬性值的類型是 string 類型對象的集合。
interface Point {
x: number;
y: number;
}
interface Named {
name: string;
}在集合論中,假設(shè) A,B 是兩個集合,由所有屬于集合 A 且屬于集合 B 的元素所組成的集合,叫做集合 A 與集合 B 的交集。
當(dāng)我們對 Point 類型和 Named 類型進(jìn)行交集運(yùn)算,就會產(chǎn)生新的類型。該類型中所包含的對象既屬于 Point 類型,又屬于 Named 類型。
在 TypeScript 中為我們提供了交叉運(yùn)算符,來實(shí)現(xiàn)對多種類型進(jìn)行交叉運(yùn)算,所產(chǎn)生的新類型也被稱為交叉類型。
下面我們來簡單介紹一下交叉運(yùn)算符,該運(yùn)算符滿足以下這些特性:
- 唯一性:A & A 等價于 A
- 滿足交換律:A & B 等價于 B & A
- 滿足結(jié)合律:(A & B) & C 等價于 A & (B & C)
- 父類型收斂:如果 B 是 A 的父類型,則 A & B 將被收斂成 A 類型
type A0 = 1 & number; // 1
type A1 = "1" & string; // "1"
type A2 = true & boolean; // true
type A3 = any & 1; // any
type A4 = any & boolean; // any
type A5 = any & never; // never
在以上代碼中,any 類型和 never 類型比較特殊。除了 never 類型之外,任何類型與 any 類型進(jìn)行交叉運(yùn)算的結(jié)果都是 any 類型。
介紹完交叉運(yùn)算符之后,我們來看一下對 Point 類型和 Named 類型進(jìn)行交叉運(yùn)算后,將產(chǎn)生什么樣的類型?
interface Point {
x: number;
y: number;
}
interface Named {
name: string;
}
type NamedPoint = Point & Named
// {
//. x: number;
//. y: number;
//. name: string;
//. }在以上代碼中,新產(chǎn)生的 NamedPoint 類型將會同時包含 x、y 和 name 屬性。但如果進(jìn)行交叉運(yùn)算的多個對象類型中,包含相同的屬性但屬性的類型不一致結(jié)果又會是怎樣呢?
interface X {
c: string;
d: string;
}
interface Y {
c: number;
e: string
}
type XY = X & Y;
type YX = Y & X;在以上代碼中,接口 X 和接口 Y 都含有一個相同的 c 屬性,但它們的類型不一致。對于這種情況,此時 XY 類型或 YX 類型中 c 屬性的類型是不是可以是 string 或 number 類型呢?下面我們來驗(yàn)證一下:
let p: XY = { c: "c", d: "d", e: "e" }; // Error
let q: YX = { c: 6, d: "d", e: "e" }; // Error為什么接口 X 和接口 Y 進(jìn)行交叉運(yùn)算后,c 屬性的類型會變成 never 呢?這是因?yàn)檫\(yùn)算后 c 屬性的類型為 string & number,即 c 屬性的類型既可以是 string 類型又可以是 number 類型。很明顯這種類型是不存在的,所以運(yùn)算后 c 屬性的類型為 never 類型。
在前面示例中,剛好接口 X 和接口 Y 中 c 屬性的類型都是基本數(shù)據(jù)類型。那么如果不同的對象類型中含有相同的屬性,且屬性類型是非基本數(shù)據(jù)類型的話,結(jié)果又會是怎樣呢?我們來看個具體的例子:
interface D { d: boolean; }
interface E { e: string; }
interface F { f: number; }
interface A { x: D; }
interface B { x: E; }
interface C { x: F; }
type ABC = A & B & C;
let abc: ABC = { // Ok
x: {
d: true,
e: '阿寶哥',
f: 666
}
};由以上結(jié)果可知,在對多個類型進(jìn)行交叉運(yùn)算時,若存在相同的屬性且屬性類型是對象類型,那么屬性會按照對應(yīng)的規(guī)則進(jìn)行合并。
但需要注意的是,在對對象類型進(jìn)行交叉運(yùn)算的時候,如果對象中相同的屬性被認(rèn)為是可辨識的屬性,即屬性的類型是字面量類型或字面量類型組成的聯(lián)合類型,那么最終的運(yùn)算結(jié)果將是 never 類型:
type A = { kind: 'a', foo: string };
type B = { kind: 'b', foo: number };
type C = { kind: 'c', foo: number };
type AB = A & B; // never
type BC = B & C; // never在以上代碼中,A、B、C 三種對象類型都含有 kind 屬性且屬性的類型都是字符串字面量類型,所以 AB 類型和 BC 類型最終都是 never 類型。接下來,我們來繼續(xù)看個例子:
type Foo = {
name: string,
age: number
}
type Bar = {
name: number,
age: number
}
type Baz = Foo & Bar
// {
//. name: never;
//. age: number;
// }在以上代碼中,Baz 類型是含有 name 屬性和 age 屬性的對象類型,其中 name 屬性的類型是 never 類型,而 age 屬性的類型是 number 類型。
但如果把 Foo 類型中 name 屬性的類型改成 boolean 類型的話,Baz 類型將會變成 never 類型。這是因?yàn)?boolean 類型可以理解成由 true 和 false 字面量類型組成的聯(lián)合類型。
type Foo = {
name: boolean, // true | false
age: number
}
type Bar = {
name: number,
age: number
}
type Baz = Foo & Bar // never其實(shí)除了對象類型可以進(jìn)行交叉運(yùn)算外,函數(shù)類型也可以進(jìn)行交叉運(yùn)算:
type F1 = (a: string, b: string) => void;
type F2 = (a: number, b: number) => void;
let f: F1 & F2 = (a: string | number, b: string | number) => { };
f("hello", "world"); // Ok
f(1, 2); // Ok
f(1, "test"); // Error
對于以上代碼中的函數(shù)調(diào)用語句,只有 f(1, "test") 的調(diào)用語句會出現(xiàn)錯誤,其對應(yīng)的錯誤信息如下:
沒有與此調(diào)用匹配的重載。
第 1 個重載(共 2 個),“(a: string, b: string): void”,出現(xiàn)以下錯誤。
類型“number”的參數(shù)不能賦給類型“string”的參數(shù)。
第 2 個重載(共 2 個),“(a: number, b: number): void”,出現(xiàn)以下錯誤。
類型“string”的參數(shù)不能賦給類型“number”的參數(shù)。ts(2769)
根據(jù)以上的錯誤信息,我們可以了解到 TypeScript 編譯器會利用函數(shù)重載的特性來實(shí)現(xiàn)不同函數(shù)類型的交叉運(yùn)算,要解決上述問題,我們可以在定義一個新的函數(shù)類型 F3,具體如下:
type F1 = (a: string, b: string) => void;
type F2 = (a: number, b: number) => void;
type F3 = (a: number, b: string) => void;
let f: F1 & F2 & F3 = (a: string | number, b: string | number) => { };
f("hello", "world"); // Ok
f(1, 2); // Ok
f(1, "test"); // Ok
掌握了交叉類型之后,在結(jié)合往期文章中介紹的映射類型,我們就可以根據(jù)工作需要實(shí)現(xiàn)一些自定義工具類型了。比如實(shí)現(xiàn)一個 PartialByKeys 工具類型,用于把對象類型中指定的 keys 變成可選的。
type User = {
id: number;
name: string;
age: number;
}
type PartialByKeys = Simplify<{
[P in K]?: T[P]
} & Pick>>
type U1 = PartialByKeys
type U2 = PartialByKeys 那么如果讓你實(shí)現(xiàn)一個 RequiredByKeys 工具類型,用于把對象類型中指定的 keys 變成必填的,你知道怎么實(shí)現(xiàn)么?知道答案的話,你喜歡以這種形式學(xué) TS 么?
網(wǎng)站欄目:同事看完這幾道題,發(fā)現(xiàn) TS 交叉類型竟還沒入門!
網(wǎng)站地址:http://m.fisionsoft.com.cn/article/dpiepse.html


咨詢
建站咨詢
