> For the complete documentation index, see [llms.txt](https://1425816423.gitbook.io/my-knowledge-base/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://1425816423.gitbook.io/my-knowledge-base/qian-duan-ji-shu/typescript/typescript-shou-xie-shi-xian-gong-ju-lei-xing.md).

# Typescript手写实现工具类型

## Typescript手写工具类型

## 起因

之前有次面试的时候和面试官说了句熟悉Typescript，

面试官：那你手写实现一下Pick类型

我：………

面试官：……..

还是要保持一颗谦虚学习的心🤣🤣🤣🤣🤣

## 工具类型

Typescript提供一些官方工具类型，这些工具类型**全局可用，** 无需手动导入，通过使用这些工具类型可以减少很多重复工作，提高效率和代码整洁度。

Typescript工具类型详细介绍可以见TypeScript实用工具

## 手写工具类型

### Pick\<Type, Keys>

```tsx
type MyPick<Type, Keys extends keyof Type> = {
    [P in Keys]: Type[P]
}
```

[demo](https://www.typescriptlang.org/zh/play?ssl=3\&ssc=2\&pln=1\&pc=1#code/C4TwDgpgBAsiAKBLAxgawDwBVwQDRQGkIQBnKCAD2AgDsATM1YgewDMptIA+KAXigDeAKCiioAbXhRENQsRIBdAFwcckhUIC+QoTOoAnVgENk0ACIQAts0EixNI5YgqSwfTIDmAbjuijH5ygaAFdLACMIfS0dZGYaVyhqVyU4JDR0C2t8AHIHJ2yefmExIMdA7OytIA)

### Omit\<Type, Keys>

```tsx
type MyOmit<Type, Keys extends keyof Type> = {
    [P in Exclude<keyof Type, Keys>]: Type[P]
}
```

Omit的作用与Pick相反，也可以借助Pick来实现Omit。

```tsx
type MyOmit<Type, Keys extends keyof Type>  = Pick<Type, Exclude<keyof Type, Keys>>
```

[demo](https://www.typescriptlang.org/zh/play?#code/PTAEBcE8AcFNQLKQPIFsCW4A8AVGsAaUAaVkgGdRYAPcWAOwBNKBrMgewDNQ84A+UAF5QAbwBQIUFKkBtAAqh09UAFFqAYwA2AV0awsbSFx74ipCnwC6ALhNx5liWAC+YqHEQoM2XoRJlKGjomVg5uXwEhUDl0dRZcU1UNHT0DMLs-c3I+PjExJToAJ04AQ3V4ABFYVHZRMWlQehLUWFtycEKlAHMAbnrpEq7Wxu1UACNYQrFXMXV2enaIWHbrJDRMLCqaogByJpadgWFxBv3hnZ2CfqlB4YBGAA5poA)

### Recod\<K, T>

```tsx
type MyRecord<K extends string | number | symbol, T> = {
    [R in K]: T
}
```

[demo](https://www.typescriptlang.org/zh/play?#code/C4TwDgpgBAsiBKEDGB7ATgEwDwGkoQA9gIA7DAZynODQEsSBzKAHyhIFcBbAIwjRaogeKADYAaKABUAfFAC8UAN4AoKGqgBteFHpQcAXQBcU5QF9ly+sTQAzAIZJoAEQicUS1epJ3OEY9TpGAG5PNTsGPzYuXjQQ82VUEmooYmpDOERUTCwAcm9fHIEc8IgciQD6BlkFFXU2H0iOEXFQqBLjHJyzIA)

### Exclude\<T, U>

```tsx
type MyExclude<T, U> = T extends U ? never : T;
```

[demo](https://www.typescriptlang.org/zh/play?#code/C4TwDgpgBAsiCiAPAxgGwK4BMIB4AqANFAKoB8UAvFHlBIsBAHaYDOJUA-FIxAG4QAnKAC5qAbgBQE5AHtGLYFAYLhcJGiy4FAgJaMA5lAA+3dAFsARoOOnUqItr2GTjc1YHkqt1EA)

### Extract\<T, U>

```tsx
type MyExtract<T, U> = T extends U ? T : never;
```

[demo](https://www.typescriptlang.org/zh/play?ssl=7\&ssc=76\&pln=7\&pc=51#code/PTAEBcE8AcFNQLKQKIA9wCcCGBjcAeAFQBpQBVAPlAF5RDRZ1YA7AEwGdzQB+O0ALlDNYAN1gYA3ACgocRCnTY8RUpRqg0OADYBXVrBUbU2vQZLkKFaVJwB7Zu3ARYjgIz8kaTLgKOMAS2YAc1AAHyEdAFsAI3EwiK0tUj9AkPDmKNiMKlpQAHI8m3tHZ0cAJg8Fb2UU4PiMmLj0nUTkzFT6zPEc0FAABiKHJ3AXcABmSq8lX3a65saMTtbQWrSIhZ6ErUGSkccAFknFH3xVzoWlpJXZtYaszZ02WAAzQNhWIA)

### NonNullable

```tsx
type MyNonNullable<T> = Exclude<T, undefined | null>
// 即
type MyNonNullable<T> = T extends null | undefined ? never : T;
// 或者
type MyNonNullable<T> = T & {}
```

[demo](https://www.typescriptlang.org/zh/play?#code/PTAEBcE8AcFNQLKQHIHsB2yCuAbHBDAIx1gB4AVAPlAF5RzRYAPcWdAEwGdR1cdQAPqCwdYAMwCW6WO1AB+HrABusAE6gAXPQDcAKBAQY8JGkx8iJCtToBRJgGMcWdmXIAaYaMnTZQ3nkpdKDhEFAxsPAtXa3pQADJQAG8AX11dewxOcAhYLIBGDRNw82IyLNUpAHNBHj4akRdvGRiAcn8cFvTM7NYsgCZCsLNI0tJyqpr2+q8pZtpavC70LJysgGZB0wiCUfH0ar86oQbxWfYYk6b2IA)

### Required

```tsx
type MyRequired<T> = {
    [K in keyof T]-?: T[K]
}
```

这里的`-?`符号看起来很容易让人懵逼，事实上这的`-` 号是控制映射类型修饰符的，除此之外还有`+`号，**可以通过`-`和`+`号来修改属性readonly或者属性可选。** 也就是说上面的`-?`意思是`remove ?` ，同理，`+?`、`+readonly`分别是添加属性可选、添加属性可读的意思，但是通常情况下我们可以省略`+`，因为不写和写`+`的效果是一样的。

关于这部分内容可见： <https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#improved-control-over-mapped-type-modifiers>

[demo](https://www.typescriptlang.org/zh/play?#code/C4TwDgpgBAsiBKECOBXAlgJwgEwDwBUA+KAXigG8AoKGqAbQGko0A7KAawhAHsAzKfAF0AtAH4AXAMaDKAX0qVWwCBl4BDAMbQAIhAC23CtVos1eiJIDOwDKwDmAbmM01di1BYo9AIxVP5lBrcLNZQytbiiKiYOLgACmoYwGhqADa4ugaExGRUtB5m7gDkRXJAA)

### Partial

根据上面的介绍，我们可以很容易想到利用`+?`来实现Partial，也可以省略`+`。

```tsx
type MyPartial<T> = {
    [K in keyof T]?: T[K]
}
```

[demo](https://www.typescriptlang.org/zh/play?#code/C4TwDgpgBAsiAKBDATsAlogNgHgCoD4oBeKAbwCgoqoBtAaSjQDsoBrCEAewDMpcBdANQB+AFx96-cgF9y5ZsAjJuiAMbQAIhAC2nMpWpNE2iOIDOwZMwDmAbgNVE101CYBXbQCMl92eVWcTBZQihaicEioGDhauoQkFNSuxi4A5KkyQA)

### Readonly

```tsx
type MyReadonly<T> = {
    +readonly [K in keyof T]: T[K]
}
```

我们也可以写出移除readonly属性的工具类型：

```tsx
type RemoveReadonly<T> = {
    -readonly [K in keyof T]: T[K]
}
```

甚至可以实现一个让数组里每个元素都readonly的工具类型。

```tsx
type MyReadonlyArray<T> = {
    +readonly [P in number]: T
}
```

[demo](https://www.typescriptlang.org/zh/play?#code/JYOwLgpgTgZghgYwgAgCIQLYHtkG8BQyRyIcGEAXMgM5hSgDmA3IcXA5SQK4YBG0LAL758AelHI4AEyn4wATwAOKALLyAShGlYQAG3kAeACoA+ZAF48rIgGooWqTv3IA2gGlkoZAGsI8rDDIRgC6VEbuwfjCCDq0yJC0FGqa2nqG6NhmlgTEJGScAOQFADTWkhxUAIwAHFFyELQAdOwQNjYi4sj22ABuEHJKKJq9ECmOacZZVrkAtPapzu6eID5+AUGhQRF1MSBxCWAATBTDWH1jToYXExlYJlM5xKTkVABEr6W5LVW1wgeHzQ4bQ6EjgUCgcHkA2UyGSDkuAEFwZDJhZpsQ7PC0q4AArLbh8aCbIw7WJgeINMAAZiSGix+iREMMtHoIAYUxcRUiBypLgADMFkDZLAVKgUgA)

### Parameters

```tsx
type MyParameters<T extends (...args: any[])=>any> = T extends (...args: infer P)=>any ? P : never
```

### ReturnType

```tsx
type MyReturnType<T extends (...args: any[]) => any> = T extends (...args: any) => infer P ? P : any
```

### ConstructorParameters

```tsx
type MyConstructorParameters<T extends abstract new (...args: any[]) => any> = T extends abstract new (...args: infer P) => any ? P : never
```

[demo](https://www.typescriptlang.org/zh/play?#code/MYGwhgzhAEAiCmBbA9tA3gKGt6YBc0EALgE4CWAdgOYDcWOARgPwEUCuiD8JdO0wyCsRJtgRZCQAU+QqUpUANNGasOXEgEp09PtiIALMhAB0YaAF5cvXXsMmGF5TugBfDG4xEAngAd40AFkvAGFBYVFxEgAFMBIwRHgibggAHgAVaHgADySKABMYMAZhMDFoCngAd2hJYzrYqggCMAovAG0AXS1zAD5cVr7LDOzcgtxi0lKicqqautMSRoJKADNuaCjuvpavaCYN6FZ4ADduDAB6c+hvP0CvAEkhIhbgeDTfeHTMnPh8wom4mUKtVavVFk1+l4tpDBtBhj8-uMSkDZqCFktIdDVusAEp7aB45qtDAYARPQgEIKhJ4iMQSGJxBJJEipG7wZArOBIZCwtoAcj5CgAjB0SUA)

### InstanceType

```tsx
type MyInstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any
```

[demo](https://www.typescriptlang.org/zh/play?#code/MYGwhgzhAEAiCmBbA9tA3gKGt6YBc0EALgE4CWAdgOYDcWOARgPwEUCuiD8JdO0wyCsRJtgRZCQAU+QqUpUANNGasOXEgEp09PtiIALMhAB0YaAF5cvXXsMmGF5TugBfDG4wB6T9CIBPAAd4aABZPwBhQWFRcRIABTASMER4Im4IAB4AFWh4AA80igATGDAGYTAxaAp4AHdoSWMmxKoIAjAKPwBtAF0tcwA+XE6hyxz8wpLcctJKomq6hqbTElaCSgAzbmg4-qGOv2gmHehWeAA3bgx-INC-AEkhIg7geCzA+Gzcgvhi0pmklUavVGs1Vm1hn49pDRtBxj8-tMKkDFqCVmtIdDNtsAEpHaB49qdDAYARPQgEMKPYgvN4fDI3eDIDZwJDIWGYPgyABE3PcJIwQA)

### ThisParameterType

```tsx
type MyThisParameterType<T> = T extends (this: infer P, ...args:never)=>any ? P : unknown;
```

[demo](https://www.typescriptlang.org/zh/play?#code/GYVwdgxgLglg9mABAEQKYFs4AooAsYDOAXIgHIjoBGqATgDSJgCG6qJBUNMYA5gJSIA3gChEiGqiggaSPIQB0UOAGVO3HlgEBqRi1QBuYQF9hwqAE8ADqkQBZcwBV8BAApMaeqLQdXUAHgcAPkQAXkQHRFQADy8wABMCRBxnEm5gWkQXBnkc9x5iMFQAN1o+EMCmMHNEAH5MxBJwAGswOAB3MENhCAQORGI7R2c3D1YvGh9rPwtrOGAUDDhgsMK2sgpqGiwARj5TIA)

### OmitThisParameter

```tsx
type MyOmitThisParameter<T> = unknown extends ThisParameterType<T> ? T : T extends (...args: infer A) => infer R ? (...args: A) => R : T
```

[demo](https://www.typescriptlang.org/zh/play?#code/GYVwdgxgLglg9mABAEQKYFs4AooAsYDOAXIgHIjoBGqATgDSJgCG6qJBUNMYA5gJSIA3gChEYxDVRQQNJHkIA6KHADKnbjywCA1IxaoA3MIC+w4VACeAB1SIAshYDy6GFAAq+AgAUmNfVFoAHjcAPkQAXkRwAGswOAB3JFQADwCwABMCRA9CHz9WAJo3a1RgsIB+bMQSN0QUtMzELAUW3x5iRG5gWkQAQQFwsK6egCVESubWmnaSfoiwsZqzCAQORA6HZ1cc719-IMsbOGAUDDgwyNBIWAQm5lZ2dV4BQQkpGSR721NhIA)

## 参考

[Documentation - Utility Types](https://www.typescriptlang.org/docs/handbook/utility-types.html)

[Documentation - TypeScript 2.8](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#improved-control-over-mapped-type-modifiers)

[Documentation - TypeScript 4.7](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html#extends-constraints-on-infer-type-variables)
