《TypeScript 类型体操通关秘籍》笔记

1,302 阅读1分钟

模式匹配做提取

实现字符串替换:
type ReplaceStr< Str extends string, From extends string, To extends string > =
    Str extends `${infer Prefix}${From}${infer Suffix}`
        ? `${Prefix}${To}${Suffix}` 
        : Str;

递归调用循环

  • ReverseArr
type ReverseArr<Arr extends unknown[]> =
    Arr extends [infer First, ...infer Rest] 
        ? [...ReverseArr<Rest>, First] 
        : Arr;
  • RemoveItem
type RemoveItem< Arr extends unknown[], Item, Result extends unknown[] = [] > =
Arr extends [infer First, ...infer Rest] 
    ? IsEqual<First, Item> extends true
        ? RemoveItem<Rest, Item, Result> 
            : RemoveItem<Rest, Item, [...Result, First]> 
    : Result; 


type IsEqual<A, B> = (A extends B ? true : false) & (B extends A ? true : false);
  • 递归添加readonly
type DeepReadonly<Obj extends Record<string, any>> = { 
readonly [Key in keyof Obj]: 
    Obj[Key] extends object 
    ? Obj[Key] extends Function 
           ? Obj[Key] 
           : DeepReadonly<Obj[Key]>
    : Obj[Key]
    }
  • Fibonacci
type FibonacciLoop< 
    PrevArr extends unknown[], 
    CurrentArr extends unknown[], 
    IndexArr extends unknown[] = [],
    Num extends number = 1 > =   
    IndexArr['length'] extends Num
        ? CurrentArr['length'] 
        : FibonacciLoop<CurrentArr, [...PrevArr, ...CurrentArr], [...IndexArr, unknown], Num>


type Fibonacci<Num extends number> = FibonacciLoop<[1], [], [], Num>;

联合类型

  • 条件类型中如果左边的类型是联合类型,会把每个元素单独传入做计算,而右边不会。
type TestUnion<A, B = A> = A extends A ? { a: A, b: B} : never; 
type TestUnionResult = TestUnion<'a' | 'b' | 'c'>;

image.png

  • isUnion
type IsUnion<A, B = A> = 
A extends A
    ? [B] extends [A] 
        ? false 
        : true 
    : never
  • BEM
type BEM< 
Block extends string,
Element extends string[],
Modifiers extends string[] > =`${Block}__${Element[number]}--${Modifiers[number]}`;
  • AllCombinations
type Combination<A extends string, B extends string> =
    | A 
    | B 
    | `${A}${B}` 
    | `${B}${A}`;

type AllCombinations<A extends string, B extends string = A> =
    A extends A 
        ? Combination<A, AllCombinations<Exclude<B, A>>>
        : never;

类型参数 A、B 是待组合的两个联合类型,B 默认是 A 也就是同一个。

A extends A 的意义就是让联合类型每个类型单独传入做处理。

A 的处理=> A 和 B 中去掉 A 以后的所有类型组合=> Combination<A, B 去掉 A 以后的所有组合>。

而 B 去掉 A 以后的所有组合就是 AllCombinations<Exclude<B, A>>

所以全组合就是 Combination<A, AllCombinations<Exclude<B, A>>>。

  • 联合转交叉 逆变
type UnionToIntersection<U> = 
    (U extends U ? (x: U) => unknown : never)    extends     (x: infer R) => unknown 
        ? R
        : never
  • GetOptional
type GetOptional<Obj extends Record<string, any>> ={ 
    [ 
    Key in keyof Obj 
    as  {}  extends Pick<Obj, Key>  ? Key  : never
    ] 
        : Obj[Key]; 
}

type Pick<T, K extends keyof T> = { [P in K]: T[P]; }

利用Pick构造子集,当某个key是可选时,会有落入undefined的情况,会构造出{}

这时通过 Key in keyof Obj as {} extends Pick<Obj, Key>即可判断 该ket是optional

  • GetRequire
type isRequired<Key extends keyof Obj, Obj> = 
    {} extends Pick<Obj, Key> ? never : Key; 

type GetRequired<Obj extends Record<string, any>> = {
    [Key in keyof Obj as isRequired<Key, Obj>]: Obj[Key]
}
  • 去掉type中的可索引签名

type Dong = { 
    [key: string]: any;
    sleep(): void; 
}

type RemoveIndexSignature<Obj extends Record<string, any>> = { 
    [ 
        Key in keyof Obj 
            as Key extends `${infer Str}`? Str : never
    ]: Obj[Key] 
}

type result =RemoveIndexSignature<Dong>
//type result = {  sleep(): void; }

如果索引是字符串字面量类型,那么就保留,否则返回 never,代表过滤掉。

模式匹配做提取

  • GetReturnType
type GetReturnType<Func extends Function> =
Func extends (...args: unknown[]) => infer ReturnType 
            ? ReturnType 
            : never;

重新构造做变换

  • UppercaseKey
type UppercaseKey<Obj extends Record<string, any>> = {
    [
    Key in keyof Obj 
        as Uppercase<Key & string>
    ]: Obj[Key] 
}

递归复用做循环

  • StringToUnion
type StringToUnion<Str extends string> = 
    Str extends `${infer First}${infer Rest}` 
        ? First | StringToUnion<Rest>
        : never;

数组长度做计数

  • 实现减法
type BuildArray<
    Length extends number,
    Ele = unknown,
    Arr extends unknown[] = []
    >
    = Arr['length'] extends Length 
          ? Arr 
          : BuildArray<Length, Ele, [...Arr, Ele]>; 

type Subtract<Num1 extends number, Num2 extends number> = 
    BuildArray<Num1> extends [...arr1: BuildArray<Num2>, ...arr2: infer Rest]
        ? Rest['length'] 
        : never;

实践

type CurriedFunc<Params, Return> = 
    Params extends []
    ? () => Return 
    : Params extends [infer Arg] 
        ? (arg: Arg) => Return
        : Params extends [infer Arg, ...infer Rest] 
             ? (arg: Arg) => CurriedFunc<Rest, Return>
             : never; 


declare function currying<Func>(fn: Func):
        Func extends (...args: infer Params) => infer Result 
            ? CurriedFunc<Params, Result>
            : never;

Ref: 神说要有光 TypeScript 类型体操通关秘籍