[TOC]
> 在 VSCode 中 Ctrl + 點擊關鍵字,進入 `<My Projects></node_modules/typescript/lib/lib.es5.d.ts` 查看到相關的高級類型(type)實現
# 工具類型(utility types)
這些 TypeScript 預置的工具類型,在全局都是可用的。
## `Partial<T>`
## `Readonly<T>`
將傳入的屬性變為只讀選項:
```js
type Readonly<T> = { readonly [P in keyof T]: T[P] };
```
## `Record<K,T>`
## `Pick<T,K>`
## `Omit<T,K>`
使用 Pick 和 Exclude 組合實現:
```js
type Omit<T, K extends keyof T> = Pick<T, K Exclude<keyof T, K>>
```
首先這個`K extends keyof T`說明這個類型值必須為 T 類型屬性的子集,`Exclude` 的效果就是尋找在 keyof T 中有,但在 K 類型中沒有的成員,這樣就將剩余的類型過濾了出來,再去應用 `Pick`,就實現了 `Omit`。
示例:
```js
type Foo = Omit<{name: string, age: number}, 'name'> // -> { age: number }
```
## `Exclude<T,U>`
與`Extract`剛好相反,基于條件類型和`never`實現:
```js
type Exclude<T, U> = T extends U ? never : T;
```
示例:
```js
type T = Exclude<1 | 2, 1 | 3> // -> 2
```
很輕松地得出結果 `2` 根據代碼和示例我們可以推斷出 Exclude 的作用是從 T 中找出 U 中沒有的元素,換種更加貼近語義的說法其實就是從 T 中排除 U
* [`Extract<T,U>`](https://socode.pro/?docspath=handbook%2Futility-types&docscode=typescript#extracttu)
## `NonNullable<T>`
## `Parameters<T>`
## `ConstructorParameters<T>`
## `ReturnType<T>`
獲取函數類型的返回類型:
```
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
```
示例:
```
const foo = (x: number): Array<number> => {
return [x];
};
type fn = ReturnType<typeof foo>;
```
## `InstanceType<T>`
獲取構造函數類型的返回類型:
```
type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;
```
示例:
```js
class C {
x = 0;
y =0 ;
}
type T200 = InstanceType<typeof C>
```
## `Required<T>`
構造一個包含`T`集合的所有屬性為`required`的類型:
```
type Required<T> = {
[P in keyof T]-?: T[P];
};
```
示例:
```
interface Props {
a?: number;
b?: string;
};
const obj: Props = { a: 5 }; // OK
const obj2: Required<Props> = { a: 5 }; // Error: property 'b' missing
```
## `ThisParameterType`
## `OmitThisParameter`
## `ThisType<T>`
`ThisType<T>` 在`lib.es5.d.ts`實現中是空的。它是編譯器將`this`指向另一個對象的標記。[this playground](https://www.typescriptlang.org/play?#code/G4QwTgBCAO0KwQLwQHYFMDuEBqBXNAFAN4BQEEaANgFwQDkAxDNALRx0A0ZEAJiAC4hapcuQC2aAM6SQAczS06ACSqUA9jnwA6AFaSAhHW4BfLuLT8AFmp6Th3cmDTA0YSWgCyUmfNoAzXBQAY34ASzUUCAIASggRUXIrUMktCWk5NCQIJJS0nzQtSWhKUP4COjporScXN0IqnTVQlHLKhwhjExJjaJISfgBPaEyAeQAjHTQQgBEpILBQ6H41MAAeaY4IDwA+LPiqWkl+BZRZM14BEAB+WmmAbm4JKxtJG62IADIIABVLZO+hmh1p8tts7hAAPQQn6AiBqPz0HJ0CDNCBPay2FGSCDTEEeboPEg8KaUcCZIIRI6aBTUgDClOOuBCK0Jg2GdIZYCZy0gyHi6Aw602OwIxMkQVo40mMzmCyWKyFoOitzx3SAA):
```js
interface ThisType { } // ThisType<T> 是空的
```
通過`ThisType`,我們可以在這個特定位置聲明`this`的類型。
上面代碼的對象描述符是這樣的:
```js
type ObjectDescriptor<Data, Methods> = { el?: string; data?: Data; methods?: Methods & ThisType<Data & Methods>;};
```
它告訴TypeScript,在所有的`methods` 函數中,這可以訪問類型`Data`和`Methods`中的字段。
Vue 的簡約版本是這樣的:
```js
declare const Vue: VueConstructor;type VueConstructor = { new<D, M>(desc: ObjectDescriptor<D, M>): D & M)
```
> [工具類型](https://www.typescriptlang.org/docs/handbook/utility-types.html)
# 動手實現題
1. 實現一個 ts 的工具函數?`GetOnlyFnProps<T>`?,提取泛型類型 T 中字段類型是函數的工具函數,其中 T 屬于一個對象。
```js
type?GetOnlyFnKeys<T?extends?object>?=?{
?[Key?in?keyof?T]:?T[K]?extends?Function???K?:?never
}
type?GetOnlyFnProps<T?extends?object>?=?{
?[K?in?GetOnlyFnKeys<T>]:?T[K]
}
```
2. 實現一個 ts 的工具函數?`UnGenericPromise<T>`?,提取 Promise 中的泛型類型。
```js
type?UnGenericPromise<T?extends?Promise<any>>?=?T?extends?Promise<infer?U>???U?:?never
```
# 工具庫
當然了,社區也是有很多類似的工具類型,可在 npm 上搜索...
* [utility-types](https://github.com/piotrwitek/utility-types)
# 參考
> [TypeScript 高級用法詳解](https://juejin.im/post/6844904021300346887)
> [bilibili-TypeScript Masterclass](https://www.bilibili.com/video/BV157411f7Fo?p=8)