体操基础

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

  1. Partial

    // 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

        // 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

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

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

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

    
    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

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

    infer 这个东西必须用到条件语句中,用来推断。而且他有自己的作用域

  9. 实现一个判断startWith<T, U>

    // 网上流行的版本   其中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

    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

    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

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

    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中迭代基本就是模拟计算机手动压栈的过程;

Last updated