以下是 TypeScript 在前端項目中?常用的映射類型(Mapped Types),結合具體場景和代碼示例,幫助開發者高效處理復雜類型:
一、基礎映射類型
1.?Partial<T>
作用:將對象類型?T
?的所有屬性變為可選。
實現:
type Partial<T> = {[P in keyof T]?: T[P];
};
應用場景:
-
表單的初始狀態(部分字段可選)。
-
API 請求的更新參數(只需傳部分字段)。
interface User {name: string;age: number;
}type PartialUser = Partial<User>;
// { name?: string; age?: number }const initialForm: PartialUser = {}; // 合法
2.?Required<T>
作用:將對象類型?T
?的所有屬性變為必填。
實現:
type Required<T> = {[P in keyof T]-?: T[P]; // "-?" 表示移除可選修飾符
};
應用場景:
-
嚴格校驗配置對象(確保所有字段必須存在)。
type StrictConfig = Required<{ apiUrl?: string }>;
// { apiUrl: string }const config: StrictConfig = { apiUrl: "https://api.com" }; // 必須填寫
3.?Readonly<T>
作用:將對象類型?T
?的所有屬性變為只讀。
實現:
type Readonly<T> = {readonly [P in keyof T]: T[P];
};
應用場景:
-
全局常量配置(防止意外修改)。
-
React 組件的默認 Props。
const defaultProps: Readonly<{ color: string }> = { color: "red" };
defaultProps.color = "blue"; // 編譯報錯:只讀屬性
4.?Pick<T, K>
作用:從對象類型?T
?中選取指定鍵?K
?的子集。
實現:
type Pick<T, K extends keyof T> = {[P in K]: T[P];
};
應用場景:
-
組件 Props 的透傳(如僅暴露部分屬性)。
-
過濾敏感字段(如排除?
password
)。
interface User {id: string;name: string;password: string;
}type SafeUser = Pick<User, "id" | "name">;
// { id: string; name: string }
5.?Omit<T, K>
作用:從對象類型?T
?中排除指定鍵?K
?的屬性。
實現:
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
應用場景:
-
覆蓋組件默認 Props。
-
忽略不需要的 API 響應字段。
type UserWithoutPassword = Omit<User, "password">;
// { id: string; name: string }
6.?Record<K, T>
作用:創建一個鍵類型為?K
,值類型為?T
?的對象類型。
實現:
type Record<K extends keyof any, T> = {[P in K]: T;
};
應用場景:
-
枚舉映射(如國際化詞表)。
-
Redux 的 Action 類型集合。
type Language = "en" | "zh";
type I18N = Record<Language, { title: string }>;const i18n: I18N = {en: { title: "Hello" },zh: { title: "你好" },
};
二、進階映射類型
1.?條件映射:{ [K in keyof T]: T[K] extends U ? X : Y }
作用:根據條件篩選或轉換屬性類型。
應用場景:
-
提取所有函數屬性。
-
將數字屬性轉為字符串。
type FunctionKeys<T> = {[K in keyof T]: T[K] extends Function ? K : never;
}[keyof T]; // 最終得到聯合類型type UserMethods = FunctionKeys<User>; // "update" | "delete"
2.?深度映射:DeepReadonly
?/?DeepPartial
作用:遞歸處理嵌套對象屬性。
實現:
type DeepReadonly<T> = {readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};type DeepPartial<T> = {[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};
應用場景:
-
深度凍結狀態(如 Redux 的不可變狀態樹)。
-
表單的嵌套字段可選。
interface NestedData {user: { name: string; address: { city: string } };
}type ReadonlyData = DeepReadonly<NestedData>;
// user 和 address 均為只讀
3.?聯合類型映射:{ [K in keyof T]: T[K] }
作用:將聯合類型轉為交叉類型或其他結構。
應用場景:
-
合并多個接口類型。
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;type Combined = UnionToIntersection<{ a: 1 } | { b: 2 }>;
// { a: 1 } & { b: 2 }
三、前端實戰場景
場景 1:動態表單控件 Props
根據表單字段類型生成對應的 UI 控件 Props。
type FieldType = "text" | "number" | "date";type FieldProps<T extends FieldType> = {type: T;value: T extends "text" ? string :T extends "number" ? number :Date;
};function renderField<T extends FieldType>(props: FieldProps<T>) {// 根據 type 渲染不同控件
}
場景 2:Redux Action 類型安全
自動推導 Action 類型,避免手動定義。
type ActionMap<M extends { [key: string]: any }> = {[Key in keyof M]: M[Key] extends undefined? { type: Key }: { type: Key; payload: M[Key] };
};type Actions = ActionMap<{LOGIN: { username: string };LOGOUT: undefined;
}>;// 結果:
// { LOGIN: { type: "LOGIN"; payload: { username: string } },
// LOGOUT: { type: "LOGOUT" } }
場景 3:組件 Props 動態擴展
通過映射類型動態生成復合 Props。
type WithClassName<T> = T & { className?: string };type ButtonProps = WithClassName<{onClick: () => void;text: string;
}>;// 結果:{ onClick: () => void; text: string; className?: string }
四、總結
映射類型 | 核心能力 | 典型場景 |
---|---|---|
Partial<T> | 屬性可選化 | 表單初始值、更新參數 |
Pick<T, K> ?/?Omit<T, K> | 屬性篩選或排除 | 組件 Props 透傳 |
Record<K, T> | 鍵值對批量定義 | 枚舉映射、Action 集合 |
條件映射 | 按條件轉換屬性類型 | 動態表單控件 |
深度映射 | 遞歸處理嵌套對象 | 不可變狀態、復雜表單 |
掌握這些映射類型后,可以大幅減少重復代碼,提升前端項目的類型安全性和可維護性