TS 有個很是好用的功能就是類型別名。javascript
類型別名會給一個類型起個新名字。類型別名有時和接口很像,可是能夠做用於原始值,聯合類型,元組以及其它任何你須要手寫的類型。html
使用類型別名能夠實現不少複雜的類型,不少複雜的類型別名都須要藉助關鍵字,咱們先來了解一下幾個經常使用的關鍵字:java
extends
能夠用來繼承一個類,也能夠用來繼承一個 interface
,但還能夠用來判斷有條件類型:node
T extends U ? X : Y; 複製代碼
上面的類型意思是,若 T
可以賦值給 U
,那麼類型是 X
,不然爲 Y
。git
原理是令 T'
和 U'
分別爲 T
和 U
的實例,並將全部類型參數替換爲 any
,若是 T'
能賦值給 U'
,則將有條件的類型解析成 X
,不然爲Y
。github
上面的官方解釋有點繞,下面舉個栗子:typescript
type Words = 'a'|'b'|"c"; type W<T> = T extends Words ? true : false; type WA = W<'a'>; // -> true type WD = W<'d'>; // -> false 複製代碼
a
能夠賦值給 Words
類型,因此 WA
爲 true
,而 d
不能賦值給 Words
類型,因此 WD
爲 false
。數組
在 JS 中 typeof
能夠判斷一個變量的基礎數據類型,在 TS 中,它還有一個做用,就是獲取一個變量的聲明類型,若是不存在,則獲取該類型的推論類型。markdown
舉兩個栗子:函數
interface Person { name: string; age: number; location?: string; } const jack: Person = { name: 'jack', age: 100 }; type Jack = typeof jack; // -> Person function foo(x: number): Array<number> { return [x]; } type F = typeof foo; // -> (x: number) => number[] 複製代碼
Jack
這個類型別名實際上就是 jack
的類型 Person
,而 F
的類型就是 TS 本身推導出來的 foo
的類型 (x: number) => number[]
。
keyof
能夠用來取得一個對象接口的全部 key 值:
interface Person { name: string; age: number; location?: string; } type K1 = keyof Person; // "name" | "age" | "location" type K2 = keyof Person[]; // "length" | "push" | "pop" | "concat" | ... type K3 = keyof { [x: string]: Person }; // string | number 複製代碼
in
能夠遍歷枚舉類型:
type Keys = "a" | "b" type Obj = { [p in Keys]: any } // -> { a: any, b: any } 複製代碼
上面 in
遍歷 Keys
,併爲每一個值賦予 any
類型。
在條件類型語句中, 能夠用 infer
聲明一個類型變量而且對它進行使用,
咱們能夠用它獲取函數的返回類型, 源碼以下:
type ReturnType<T> = T extends ( ...args: any[] ) => infer R ? R : any; 複製代碼
其實這裏的 infer R
就是聲明一個變量來承載傳入函數簽名的返回值類型, 簡單說就是用它取到函數返回值的類型方便以後使用。
下面咱們看一下 TS 內置的一些類型別名:
Partial
的做用就是能夠將某個類型裏的屬性所有變爲可選項 ?
。
源碼:
// node_modules/typescript/lib/lib.es5.d.ts type Partial<T> = { [P in keyof T]?: T[P]; }; 複製代碼
從源碼能夠看到 keyof T
拿到 T
全部屬性名, 而後 in
進行遍歷, 將值賦給 P
, 最後 T[P]
取得相應屬性的值. 結合中間的 ?
,將全部屬性變爲可選.
Required
的做用恰好跟 Partial
相反,Partial
是將全部屬性改爲可選項,Required
則是將全部類型改爲必選項,源碼以下:
// node_modules/typescript/lib/lib.es5.d.ts type Required<T> = { [P in keyof T]-?: T[P]; }; 複製代碼
其中 -?
是表明移除 ?
這個 modifier 的標識。
與之對應的還有個 +?
, 這個含義天然與 -?
以前相反, 它是用來把屬性變成可選項的,+
可省略,見 Partial
。
再拓展一下,除了能夠應用於 ?
這個 modifiers ,還有應用在 readonly
,好比 Readonly
.
這個類型的做用是將傳入的屬性變爲只讀選項。
// node_modules/typescript/lib/lib.es5.d.ts type Readonly<T> = { readonly [P in keyof T]: T[P]; }; 複製代碼
給子屬性添加 readonly
的標識,若是將上面的 readonly
改爲 -readonly
, 就是移除子屬性的 readonly
標識。
這個類型則能夠將某個類型中的子屬性挑出來,變成包含這個類型部分屬性的子類型。
源碼實現以下:
// node_modules/typescript/lib/lib.es5.d.ts type Pick<T, K extends keyof T> = { [P in K]: T[P]; }; 複製代碼
從源碼能夠看到 K
必須是 T
的 key,而後用 in
進行遍歷, 將值賦給 P
, 最後 T[P]
取得相應屬性的值。
該類型能夠將 K
中全部的屬性的值轉化爲 T
類型,源碼實現以下:
// node_modules/typescript/lib/lib.es5.d.ts type Record<K extends keyof any, T> = { [P in K]: T; }; 複製代碼
能夠根據 K
中的全部可能值來設置 key,以及 value 的類型,舉個例子:
type T11 = Record<'a' | 'b' | 'c', Person>; // -> { a: Person; b: Person; c: Person; } 複製代碼
Exclude
將某個類型中屬於另外一個的類型移除掉。
源碼的實現:
// node_modules/typescript/lib/lib.es5.d.ts type Exclude<T, U> = T extends U ? never : T; 複製代碼
以上語句的意思就是 若是 T
能賦值給 U
類型的話,那麼就會返回 never
類型,不然返回 T
,最終結果是將 T
中的某些屬於 U
的類型移除掉,舉個例子:
type T00 = Exclude<'a' | 'b' | 'c' | 'd', 'a' | 'c' | 'f'>; // -> 'b' | 'd' 複製代碼
能夠看到 T
是 'a' | 'b' | 'c' | 'd'
,而後 U
是 'a' | 'c' | 'f'
,返回的新類型就能夠將 U
中的類型給移除掉,也就是 'b' | 'd'
了。
Extract
的做用是提取出 T
包含在 U
中的元素,換種更加貼近語義的說法就是從 T
中提取出 U
,源碼以下:
// node_modules/typescript/lib/lib.es5.d.ts type Extract<T, U> = T extends U ? T : never; 複製代碼
以上語句的意思就是 若是 T
能賦值給 U
類型的話,那麼就會返回 T
類型,不然返回 never
,最終結果是將 T
和 U
中共有的屬性提取出來,舉個例子:
type T01 = Extract<'a' | 'b' | 'c' | 'd', 'a' | 'c' | 'f'>; // -> 'a' | 'c' 複製代碼
能夠看到 T
是 'a' | 'b' | 'c' | 'd'
,而後 U
是 'a' | 'c' | 'f'
,返回的新類型就能夠將 T
和 U
中共有的屬性提取出來,也就是 'a' | 'c'
了。
該類型的做用是獲取函數的返回類型。
源碼的實現
// node_modules/typescript/lib/lib.es5.d.ts type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any; 複製代碼
實際使用的話,就能夠經過 ReturnType
拿到函數的返回類型,以下的示例:
function foo(x: number): Array<number> { return [x]; } type fn = ReturnType<typeof foo>; // -> number[] 複製代碼
這個類型是用於指定上下文對象類型的。
// node_modules/typescript/lib/lib.es5.d.ts interface ThisType<T> { } 複製代碼
能夠看到聲明中只有一個接口,沒有任何的實現,說明這個類型是在 TS 源碼層面支持的,而不是經過類型變換。
這類型怎麼用呢,舉個例子:
interface Person { name: string; age: number; } const obj: ThisType<Person> = { dosth() { this.name // string } } 複製代碼
這樣的話,就能夠指定 obj
裏的全部方法裏的上下文對象改爲 Person
這個類型了。
該類型的做用是獲取構造函數類型的實例類型。
源碼實現:
// node_modules/typescript/lib/lib.es5.d.ts type InstanceType<T extends new (...args: any[]) => any> = T extends new (...args: any[]) => infer R ? R : any; 複製代碼
看一下官方的例子:
class C { x = 0; y = 0; } type T20 = InstanceType<typeof C>; // C type T21 = InstanceType<any>; // any type T22 = InstanceType<never>; // any type T23 = InstanceType<string>; // Error type T24 = InstanceType<Function>; // Error 複製代碼
這個類型能夠用來過濾類型中的 null
及 undefined
類型。
源碼實現:
// node_modules/typescript/lib/lib.es5.d.ts type NonNullable<T> = T extends null | undefined ? never : T; 複製代碼
好比:
type T22 = string | number | null; type T23 = NonNullable<T22>; // -> string | number; 複製代碼
該類型能夠得到函數的參數類型組成的元組類型。
源碼實現:
// node_modules/typescript/lib/lib.es5.d.ts type Parameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never; 複製代碼
舉個栗子:
function foo(x: number): Array<number> { return [x]; } type P = Parameters<typeof foo>; // -> [number] 複製代碼
此時 P
的真實類型就是 foo
的參數組成的元組類型 [number]
。
該類型的做用是得到類的參數類型組成的元組類型,源碼:
// node_modules/typescript/lib/lib.es5.d.ts type ConstructorParameters<T extends new (...args: any[]) => any> = T extends new (...args: infer P) => any ? P : never; 複製代碼
舉個栗子:
class Person { private firstName: string; private lastName: string; constructor(firstName: string, lastName: string) { this.firstName = firstName; this.lastName = lastName; } } type P = ConstructorParameters<typeof Person>; // -> [string, string] 複製代碼
此時 P
就是 Person
中 constructor
的參數 firstName
和 lastName
的類型所組成的元組類型 [string, string]
。
下面是一些可能會常常用到,可是 TS 沒有內置的一些類型別名:
有時候咱們想要繼承某個接口,可是又須要在新接口中將某個屬性給 overwrite 掉,這時候經過 Pick
和 Exclude
就能夠組合出來 Omit
,用來忽略對象某些屬性功能:
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>; // 使用 type Foo = Omit<{name: string, age: number}, 'name'> // -> { age: number } 複製代碼
將 T 的全部屬性的 readonly
移除:
type Mutable<T> = { -readonly [P in keyof T]: T[P] } 複製代碼
內置的 Partial 有個侷限性,就是隻支持處理第一層的屬性,若是是嵌套多層的就沒有效果了,不過能夠以下自定義:
type PowerPartial<T> = { // 若是是 object,則遞歸類型 [U in keyof T]?: T[U] extends object ? PowerPartial<T[U]> : T[U] }; 複製代碼
相同的屬性名稱,但使值是一個 Promise
,而不是一個具體的值:
type Deferred<T> = { [P in keyof T]: Promise<T[P]>; }; 複製代碼
爲 T
的屬性添加代理
type Proxify<T> = { [P in keyof T]: { get(): T[P]; set(v: T[P]): void } }; 複製代碼
若有疑問,歡迎斧正!