TypeScript 學習筆記 — 自定義型別:部分屬性可選,反選 key,求物件交差並補集等(十三)

Echoyya、發表於2023-04-06


上文中介紹了 物件操作的內建型別的使用,本文介紹幾個基於內建型別,擴充套件的自定義型別,(型別名稱自定義的)

將部分屬性變為可選屬性

實現思路:先將 name 屬性挑出來變為可選的 & 除了 name 屬性的

// 有時處理型別之後,物件型別結構不明顯。只是簡單做一個對映,並未其他意義。
type Compute<T extends object> = {
  [K in keyof T]: T[K]; // 對映
};
interface Person {
  name: string;
  age: number;
  address: string;
}
// 先將name屬性挑出來變為可選的  &   除了name屬性的
//  Partial<Pick<T, K>>        &   & Omit<T, K>

type PartialPropsOption<T, K extends keyof T> = Partial<Pick<T, K>> & Omit<T, K>;

type x1 = PartialPropsOption<Person, "name">;
type x2 = Compute<PartialPropsOption<Person, "name">>; // 使用Compute包一層可以看清楚結構,不包也可以

根據值的型別 反選 key

之前的常見做法都是用過 key 值來獲取屬性,反之,如果根據值的型別 反選 key?

寫法一:基礎原理寫法,使用不同的內建型別,Pick 和 Omit

定義一個介面 Person。透過不同的內建型別,實現挑選 值型別為string的keys過濾 值型別為string的keys

interface Person {
  name: string;
  age: number;
  address: string;
}
// 判斷兩個型別是否相等
type isEqual<T, U, Success, Fail> = [T] extends [U] ? ([U] extends [T] ? Success : Fail) : Fail;

type ExtractKeysByValueType<T, U> = {
  [K in keyof T]: isEqual<T[K], U, K, never>;
}[keyof T]; // 找到需要的屬性 name | address   **注:聯合型別中沒有never

type PickKeysByValue<T, U> = Pick<T, ExtractKeysByValueType<T, U>>;
type PickedKeys = PickKeysByValue<Person, string>; // 挑選 值型別為string的keys 返回 name address

type OmitKeysByValue<T, U> = Omit<T, ExtractKeysByValueType<T, U>>;
type OmitedKeys = OmitKeysByValue<Person, string>; // 過濾 值型別為string的keys 返回 age

寫法二:基礎原理寫法,使用 Pick 內建型別 + 傳參的方式

interface Person {
  name: string;
  age: number;
  address: string;
}
// 判斷兩個型別是否相等
type isEqual<T, U, Success, Fail> = [T] extends [U] ? ([U] extends [T] ? Success : Fail) : Fail;

type ExtractKeysByValueType<T, U, O = false> = {
  [K in keyof T]: isEqual<T[K], U, isEqual<O, true, never, K>, isEqual<O, true, K, never>>;
}[keyof T]; // 找到需要的屬性 name | address   **注:聯合型別中沒有never

type PickKeysByValue<T, U> = Pick<T, ExtractKeysByValueType<T, U>>;
type PickedKeys = PickKeysByValue<Person, string>; // 挑選 值型別為string的keys 返回 name address

type OmitKeysByValue<T, U> = Pick<T, ExtractKeysByValueType<T, U, true>>;
type OmitedKeys = OmitKeysByValue<Person, string>; // 過濾 值型別為string的keys 返回 age

寫法三:使用對映條件做雙重對映

interface Person {
  name: string;
  age: number;
  address: string;
}
type PickKeysByValue<T extends object, U> = {
  // [K in keyof T as `a_${K & string}`]: T[K];   // 使用模板字串 重新命名

  // as 語法 對映成一個新的變數
  [K in keyof T as T[K] extends U ? K : never]: T[K];
};

type PickKeysByAs = PickKeysByValue<Person, string>; // 挑選 值型別為string的keys 返回 name address

求物件的交集 ObjectInter

交集:指的是型別中的屬性屬性屬性,即存在於 A 中,又存在於 B 中,屬性的型別可以不同,取交集後者型別,

type A = {
  name: string;
  age: number;
  address: string;
};

type B = {
  name: string;
  male: boolean;
  address: number;
};
// 返回的是聯合型別 name | address
type ObjectInter<T extends object, U extends object> = Pick<U, Extract<keyof T, keyof U>>;

type X1 = ObjectInter<A, B>; // 從B中取A,address型別取B中的型別 number

求物件的差集 ObjectDiff

算一下 B - A,使用內建型別組合:Omit + Extract == Pick + Exclude
組合的寫法比較多種,只要可以實現就可以,需要注意的是 誰-誰,就是從哪個物件中挑選

type A = {
  name: string;
  age: number;
  address: string;
};

type B = {
  name: string;
  male: boolean;
  address: number;
};
type ObjectDiff<T extends object, U extends object> = Omit<U, Extract<keyof T, keyof U>>;
type X2 = ObjectDiff<A, B>; // B - A = male: boolean;

type ObjectDiff2<T extends object, U extends object> = Pick<T, Exclude<keyof T, keyof U>>;
type X3 = ObjectDiff2<A, B>; // A - B = age: number;

type OmitDiff<T extends object, U extends Object> = Omit<T, keyof U>;
type X4 = OmitDiff<A, B>; // A - B = age: number;

求物件的並集 ObjectMerge

希望最終得到的結果是 type X7 = { age: number; name: string; address: number; male: boolean;}
實現思路:先找出A - B的差集,再& B

type A = {
  name: string;
  age: number;
  address: string;
};

type B = {
  name: string;
  address: number;
  male: boolean;
};
type Compute<T extends object> = {
  [K in keyof T]: T[K]; // 只是做了對映,為了顯示型別結果
};

type ObjectMerge<T extends object, U extends object> = Omit<T, keyof U> & U;
type X5 = Compute<ObjectMerge<A, B>>;

求物件的補集 ObjectComp

補集:存在於 A 中,不存在於 B 中,屬性相差。互補,注意:雙方得存在父子關係,才能互補 也就是 B 是 A 的子型別

type A = {
  name: string;
  age: number;
  address: string;
};

type B = {
  name: string;
  address: string;
};

// 型別“B” 需要滿足約束 “A”。
type ObjectComp<T extends object, U extends T> = Omit<U, Extract<keyof T, keyof U>>;
type X6 = ObjectComp<B, A>; // age: number;

重寫物件型別 Overwrite

使用 B 重寫 A 中相同的屬性,案例中就是 使用 address: string => address: number,其他不變
希望最終得到的結果是 A=> { name: string; age: number; address: number }
實現思路:先找出交集,在找差集,利用上邊已經實現的程式碼

type A = {
  name: string;
  age: number;
  address: string;
};

type B = {
  name: string;
  address: number;
  male: boolean;
};
type Compute<T extends object> = {
  [K in keyof T]: T[K]; // 只是做了對映,為了顯示型別結果
};

type Overwrite<T extends object, U extends object> = ObjectInter<A, B> & ObjectDiff2<A, B>;
type X7 = Compute<Overwrite<A, B>>;

相關文章