highlight: github
theme: juejin
前言
Typescript
中預設內建了很多工具泛型,透過使用這些工具,可以使得我們定義型別更加靈活,高效。本文將會介紹常用泛型工具的使用技巧,以及對其實現原理進行相應的解析,如果有錯誤的地方,還望指出。
Partial\<T>
作用:將傳入物件型別 T
的屬性變為可選屬性。
示例:
interface Person {
name: string;
age: number;
}
const tom: Partial<Person> = {
name: "Tom",
};
Partial<Person>
等價於
interface Person {
name?: string;
age?: number;
}
實現原理:
- 透過關鍵字
keyof
將傳入物件型別的鍵值轉換為聯合型別。 - 透過關鍵字
in
遍歷聯合型別,即遍歷物件的鍵值。 - 透過型別對映,將物件的屬性轉換為可選屬性
type MyPartial<T> = {
[P in keyof T]?: T[P];
};
Readonly\<T>
作用:把傳入物件型別 T
屬性變為只讀屬性。
示例:
interface Person {
name: string;
age: number;
}
const tom: Readonly<Person> = {
name: "Tom",
age: 18;
};
tom.age = 22 // error
Readonly<Person>
等價於
interface Person {
readonly name: string;
readonly age: number;
}
實現原理:
與Partial
類似:
- 透過關鍵字
keyof
將傳入物件型別的鍵值轉換為聯合型別。 - 透過關鍵字
in
遍歷聯合型別,即遍歷物件的鍵值。 - 透過型別對映,將物件的屬性轉換為只讀屬性
type Readonly<T> = {
[P in keyof T]-?: T[P];
};
Required\<T>
作用:把傳入物件型別 T
屬性變為必填屬性。
示例:
interface Person {
name?: string;
age?: number;
}
let tom: Required<Person>
tom = {
name: "Tom",
age: 18;
};
// ok
tom = {
name: "Tom",
};
// error
實現原理:
與Partial
類似:
- 透過關鍵字
keyof
將傳入物件的鍵值轉換為列舉型別。 - 透過關鍵字
in
遍歷列舉型別,即遍歷物件的鍵值。 - 透過型別對映,再統一透過
-?
修飾符移除?
修飾符,從而轉變為必填狀態。
type Required<T> = {
Required [P in keyof T]: T[P];
};
Record\<K,T>
作用:它用來生成一個屬性名為 K
,屬性值型別為 T
的物件型別集合。
示例:
// 快速生成一個 Person 物件
type Person = Record<"name" | "country", string>;
const Tom: Person = { name: "Tom", country: "America" };
實現原理:
- 透過
K extends keyof any
對K
引數進行約束,將其約束為任意型別any
的鍵值。 - 透過
in
對鍵值集合K
進行遍歷,然後生成型別為T
的鍵值對集合。
type MyRecord<K extends keyof any, T> = {
[P in K]: T;
};
Exclude\<T,K>
作用:從型別 T
中排除所有可以賦值給型別 U
的型別。
示例:
// 從 "a" | "b" | "c" 中排除掉 "a" 型別
type T1 = Exclude<"a" | "b" | "c", "a">;
// T1 = "b" | "c"
// 從 string | number | boolean 中排除掉 string 型別
type T2 = Exclude<string | number | boolean, string>;
// T2 = number | boolean
實現原理:
透過條件型別
T extends U ? never : T
對T
引數進行判別:- 如果
T
可賦值給U
,那麼返回never
(即排除掉T
)。 - 如果
T
不可賦值給U
,那麼返回T
。
- 如果
- 透過分散式條件型別,如果
T
為聯合型別,則將條件型別的結果分發為聯合型別。
type Exclude<T, U> = T extends U ? never : T;
Extract\<T,K>
作用:與 Exclude
相反,從型別 T
中提取所有可以賦值給型別 U
的型別。
示例:
// 從 "a" | "b" | "c" 中提取出 "a" 型別
type T1 = Extract<"a" | "b" | "c", "a">;
// T1 = "a"
// 從 string | number | boolean 中提取出 string 型別
type T2 = Extract<string | number | boolean, string>;
// T2 = string
type T3 = Extract<string | (() => void), Function>;
// 相當於 type T3 = () => void;
實現原理:
與 Exclude
類似:
透過條件型別
T extends U ? never : T
對T
引數進行判別:- 如果
T
可賦值給U
,那麼返回T
。 - 如果
T
不可賦值給U
,那麼返回never
(即排除掉T
)。
- 如果
- 透過分散式條件型別,如果
T
為聯合型別,則將條件型別的結果分發為聯合型別。
type Extract<T, U> = T extends U ? T : never;
Pick\<T,K>
作用:在 T
中,摘選出 K
屬性。
示例:
interface Person {
name: string;
age: number;
}
// 從 Person 中摘選出 name 屬性
type PickPerson = Pick<Person, "name">;
const tom: PickPerson = {
name: "Tom",
};
實現原理:
- 透過
K extends keyof T
對K
引數進行約束,將其約束為T
的鍵值範圍內。 - 透過
in
對鍵值集合K
進行遍歷,然後生成型別為T
的鍵值對集合。
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
Omit\<T,K>
作用:在 T
中,剔除掉 K
屬性。
示例:
interface Person {
name: string;
age: number;
}
// 從 Person 中剔除掉 name 屬性
type OmitPerson = Omit<Person, "name">;
const tom: OmitPerson = {
age: 18,
};
實現原理:
- 透過
K extends keyof T
對K
引數進行約束,將其約束為T
的鍵值範圍內。 - 透過
Exclude<keyof T, K>
將型別集合T
中的K
型別排除掉。 - 透過
Pick<T,Exclude<keyof T, K>>
在T
中摘選出排除掉K
的T
的屬性。
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
ReturnType\<T>
作用:獲取函式的返回值型別。
示例:
type Fun = () => string;
// 獲取 Fun 返回值的型別
type T1 = ReturnType<Fun>; // T1 = string
type T2 = ReturnType<() => { x: number; y: number }>;
// T2 = { x: number, y: number }
實現原理:
- 透過
extends
對T
引數進行約束,(...args: any) => any
表示一個函式型別,即T
引數的型別必須是一個函式型別。 T extends U ? X : Y
是條件型別(注意和之前表示約束的extends
做區分),其中T
是泛型引數,U
是條件部分,X
是符合條件的返回結果,Y
是不符合條件的返回結果。- 推斷型別
infer
的作用是:在條件型別內部宣告一個型別變數。(...args: any) => infer R
是條件型別的條件部分,它宣告瞭一個型別變數R
,用來儲存函式的返回型別。 T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any
表示:- 如果
T
是函式型別((...args: any) => infer R
),則返回R
, 即函式的返回型別。 - 如果
T
不是函式型別((...args: any) => infer R
),則返回any
。
- 如果
type ReturnType<T extends (...args: any) => any> = T extends (
...args: any
) => infer R
? R
: any;