跳到主要内容

複合型別 Union & Intersection

Union

type UnionSet1 = number | string;

type UserInfo1 = {
name: string,
age: number
};
type UserInfo2 = {
hasPet: boolean,
ownsMotorcycle: boolean,
};

// 按照數學推理理應只有三種組合:
// 1. 只有 UserInfo1:PASS
let maxwellOnlyInfo1: UnionSet2 = {
name: 'Maxwell',
age: 20
};

// 2. 只有 UserInfo2:PASS
let maxwellOnlyInfo2: UnionSet2 = {
hasPet: false,
ownsMotorcycle: true
};

// 3. 都有:PASS
let maxwellOnlyInfo3: UnionSet2 = {
name: 'Maxwell',
age: 20,
hasPet: false,
ownsMotorcycle: true
};

以上code與數學聯集觀念相似,But

// 理應要錯誤的組合:
type UserInfo1 = {
name: string,
age: number
};

type UserInfo2 = {
hasPet: boolean,
ownsMotorcycle: boolean,
};

type UnionSet2 = UserInfo1 | UserInfo2;
// 1. UserInfo1 和 UserInfo2 皆缺屬性:保證錯!
let maxwellWithPartialInfo1: UnionSet2 = {
name: 'Maxwell',
// age: 20, <-- 缺這個屬性
// hasPet: false, <-- 缺這個屬性
ownsMotorcycle: true
};

// 2. UserInfo1 滿足但 UserInfo2 有缺屬性:PASS
let maxwellWithPartialInfo2: UnionSet2 = {
name: 'Maxwell',
age: 20,
// hasPet: false, <-- 缺這個屬性
ownsMotorcycle: true
};

// 3. UserInfo2 滿足但 UserInfo1 有缺屬性:PASS
let maxwellWithPartialInfo3: UnionSet2 = {
// name: 'Maxwell', <-- 缺這個屬性
age: 20,
hasPet: false,
ownsMotorcycle: true
};
// 空集合一定錯
// let maxwellWithNoInfo: UnionSet2 = {};

理所當然,第一個案例一定錯,因為既不滿足 UserInfo1 也不滿足 UserInfo2;然而後續的例子,只要至少其中一個型別被判定滿足,不管其他型別有沒有完整補齊,TypeScript 認為無所謂。

其實這行為比較像OR

  • 你可以選擇只要符合 UserInfo1 要求的型別或介面格式
  • 或(OR)你可以選擇只要符合 UserInfo2 要求的型別或介面格式
  • 但你也可以全部都符合
  • 不過就是至少一個條件一定要滿足,否則出錯!因此空集合概念在這裡也不覆存在,會被認定是錯的,因為 OR 邏輯本來就是建立在其中一方符合的條件下才能滿足的。

Intersection

type UserInfo1 = {
name: string,
age: number
};

type UserInfo2 = {
hasPet: boolean,
ownsMotorcycle: boolean,
};3
type IntersectionSet = UserInfo1 & UserInfo2;

// 正確格式,所有屬性必須都出現
let correctInfo: IntersectionSet = {
name: 'Maxwell',
age: 20,
hasPet: false,
ownsMotorcycle: true
};

// 錯誤格式,屬性缺一不可
let wrongInfo1: IntersectionSet = {
// name: 'Maxwell', <-- 少一個 UserInfo1 屬性
age: 20,
hasPet: false,
ownsMotorcycle: true
};

let wrongInfo2: IntersectionSet = {
name: 'Maxwell',
age: 20,
hasPet: false,
// ownsMotorcycle: true <-- 少一個 UserInfo2 屬性
};

根據 UserInfo1UserInfo2 各自的型別格式,有沒有認真想過它們有何交集點?

nameage 以及 hasPetownsMotorcycle 的組合 —— 兩組屬性的集合中,完全沒有交集!

偏偏在 TypeScript 裡 —— 交集 intersection 的用法是:將兩個可以為型別或介面的組合裡的格式進行結合的概念。

  • 你必須符合 UserInfo1 和(AND)UserInfo2 的型別或介面格式
  • 只要少了一個屬性就會出錯,完全符合 AND 邏輯的真諦

Never

/* 原始型別複合 */
type PrimitiveIntersection = number & string;

既是數字和字串的東西,當然不存在,但是隱身於所有型別當中的共通點。

never is a subtype of and assignable to every type.

任何型別 T(包含 never 本身)和 never 進行 union ,則型別 T 會吸收掉 never 型別:

type WontBeNever = T | never;
// => WontBeNever: T

任何型別 U(包含 never 本身)和 never 進行 intersection ,則型別 U 會被 never 型別強行覆蓋:

type MustBeNever = U & never;
// => MustBeNever: never