[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)