# 体操基础

> 体操全集： <https://github.com/type-challenges/type-challenges/blob/main/README.zh-CN.md>

1. Partial

   ```typescript
   // Partial<T>将T的所有属性变成可选的。
   type Partial<T> = {
       [P in keyof T]?: T[P];
   }
   ```

   解释：

   1. in: in的用法是要用在类型遍历中形如上图这种{} 下 in 后面要是一个string、number、symbol联合类型，比如你可以keyof '1213'|'123123'|'13123' 但是不能p in \[];
   2. keyof 是将一个接口（interface）或者类型（type）转变成他的key的联合类型
   3. T\[p] 类似于js本身语法 T是个对象，P是他的key T\[p] 就是他原来的值
2. Exculde

   ```typescript
       // Exculde<T, U>排除联合类型T 中 属于U的类型
       type Exculde<T, U> = T extends U ? never : T;


   ```

   解释:

   1. never 联合其他类型是没有never的

   2. T extends U ？never : T 属于条件类型 知识点：分布式条件类型

   > 这里有个思考就是1 的解题思路是{} {}中遍历key的形式。2的解题思路是条件类型 他俩有什么区别? 答 1 是对接口中key的筛选或处理，比如Pick（筛选出符合条件的key）、Record（给key赋值），2 是对联合类型的筛选 比如Exculde(例如:将'a'|'b' 筛选成'a')
3. Require 将类型T所有属性设为require

   ```ts
   type Require<T> = {
        [p in keyof T]-?: T[p];
   }
   ```
4. Pick\<T, K> 从类型T 中挑选部分属性K 来构造新的类型

   ```ts

   type Pick<T, K> = {
        [p in keyof K]: T[p];
   }
   ```
5. Extract\<T, U> 从类型T中提取所有可以赋值给U的类型

   ```ts
   type Extract<T, U> = T extends U ? T : never;
   ```
6. Omit\<T, K> 从类型T中剔除所有能赋值给K的属性

   ```ts

   type Omit<T, K> = {
       [p in Exculde<keyof T, K>] : T[p];
   }

   // 但是这里面还有一种更有意思的写法 值得注意的是这里 as 对 T 起到了收敛子集的作用
   type Omit<T, K> = {
       [p in K as (T extends K ? never: T)] : T[p]
   }
   // 另外还有一种更长用的
   type OmitV<T, K> = {
       [p in keyof T as (T[p] extends K ? p : never)]: T[p];
   }
   ```
7. NonNullable 从T中剔除null和undefined
8. ReturnType

   ```ts
   type ReturnType<T extends (...arg: any) => any> = T extends (...arg:any) => infer R ? R : any;
   ```

   infer 这个东西必须用到条件语句中，用来推断。而且他有自己的作用域
9. 实现一个判断startWith\<T, U>

   ```ts
   // 网上流行的版本   其中string做为特殊关键字参与了结构；
   type startWith<Prefix> = `${Prefix}${string}`;

   type a = '/as'

   type b = a extends startWith<'/'> ? true; false;
   // 自己实现的版本
   type startWith<T, U extends string> = T extends `${U}${infer R}` ? true: false;

   type c = startWith<a, '/'>
   ```
10. 使用映射类型实现window\.addEvent

    ```ts
    type EventType1 = {
        target: Element;
    }

    type EventType2 = {
        target: HTMLInputElement;
    }

    type EventFunctionMap = {
        type1: EventType1;
        type2: EventType2;
    }

    type EventList = keyof EventFunctionMap;

    type addEvent<T extends EventList> = (
        type: T,
        listener: (
            arg:  EventFunctionMap[T],
        ) => void,
    ) => void;

    // 附上一个 不用映射类型的

    ```
11. PickByType

    ```ts
    type OnlyBoolean = PickByType<{
    name: string
    count: number
    isReadonly: boolean
    isEnable: boolean
    }, boolean> // { isReadonly: boolean; isEnable: boolean; }

    // 实现：
    type PickByType<T, U> = {
        [K in keyof T as T[K] extends U ? K : never]: T[K]
    }

    ```

    很有意思的是拿as 约束了 key遍历的数量
12. Mutable

    ```ts
    // Mutable 把所有的属性改为非只读，即去掉readonly 可以使用-操作符
    type Mutable<T extends object> = {
        -readonly [K in keyof T]: T[K]
    }
    ```
13. Length of String

    ```ts
    type Split<S extends string> = S extends `${infer F}${infer R}` ? [F, ...Split<R>] : []

    type LengthOfString<S extends string> = Split<S>['length']
    ```

    思路: ts支持通过length变量取数组的长度，所以核心本质是字符串转数组，然后取长度; 另外ts中迭代基本就是模拟计算机手动压栈的过程；
