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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://1425816423.gitbook.io/my-knowledge-base/qian-duan-ji-shu/typescript/typescript-shou-xie-shi-xian-gong-ju-lei-xing.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
