Featured image of post 使用映射類型動態轉換類型Featured image of post 使用映射類型動態轉換類型

使用映射類型動態轉換類型

透過映射現有介面的鍵、利用鍵重新映射和修飾符來製作動態類型。

介紹

映射類型允許您透過迭代其鍵來轉換現有物件類型。將它們視為 Array.prototype.map() 但處於 類型層級。語法 { [K in keyof T]: T[K] } 透過對 T 的每個屬性套用轉換來建立新類型。這就是 TypeScript 的許多內建實用程式類型在幕後工作的方式。


基本映射類型

最簡單的映射類型會建立現有類型的克隆:

type Clone<T> = { [K in keyof T]: T[K] };

interface User {
  name: string;
  age: number;
}

type UserClone = Clone<User>;
// { name: string; age: number }

新增修飾符

映射類型可以套用三個修飾符:readonly?(可選)和 -(刪除):

// Make all properties readonly
type MyReadonly<T> = { readonly [K in keyof T]: T[K] };

// Make all properties optional
type MyPartial<T> = { [K in keyof T]?: T[K] };

// Remove readonly (the - modifier)
type MyMutable<T> = { -readonly [K in keyof T]: T[K] };

// Remove optional
type MyRequired<T> = { [K in keyof T]-?: T[K] };

這些正是內建 Readonly<T>Partial<T>Required<T> 的實作方式。


使用 as 鍵重新映射進行過濾

TypeScript 4.1 引進了使用 as 子句的鍵重新映射。這允許過濾或重命名鍵:

// Filter keys that are strings
type StringKeys<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K];
};

interface User {
  name: string;
  age: number;
  email: string;
}

type OnlyStrings = StringKeys<User>;
// { name: string; email: string }

模板文字關鍵更改

將映射類型與 模板文字類型 結合起來以轉換鍵名稱:

// Add a prefix to all keys
type WithPrefix<T, P extends string> = {
  [K in keyof T as `${P}_${K & string}`]: T[K];
};

interface User {
  name: string;
  age: number;
}

type Prefixed = WithPrefix<User, 'user'>;
// { user_name: string; user_age: number }

轉換為 getter/setter 約定:

type Getters<T> = {
  [K in keyof T as `get${Capitalize<K & string>}`]: () => T[K];
};

type UserGetters = Getters<{ name: string; age: number }>;
// { getName: () => string; getAge: () => number }

選擇和省略實施

以下是 PickOmit 的內部運作原理:

type MyPick<T, K extends keyof T> = {
  [P in K]: T[P];
};

type MyOmit<T, K extends keyof any> = {
  [P in keyof T as P extends K ? never : P]: T[P];
};

interface User {
  name: string;
  age: number;
  email: string;
}

type NameOnly = MyPick<User, 'name'>;
// { name: string }

type NoEmail = MyOmit<User, 'email'>;
// { name: string; age: number }

實用圖案

Deep Partial(遞歸映射類型)

type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};

可空屬性

type Nullable<T> = {
  [K in keyof T]: T[K] | null;
};

使用映射重命名鍵

type RenameKeys<T, M extends Record<string, string>> = {
  [K in keyof T as K extends keyof M ? M[K] : K]: T[K];
};

結論

映射類型是 TypeScript 類型轉換功能的支柱。從基本修飾符(readonly?)到使用模板文字的高級鍵重新映射,它們使您可以對類型如何演變進行細粒度控制。與條件類型結合,它們實現了編譯時元編程,使 TypeScript 真正強大。