对于提取或构造的数组元素个数不确定、字符串长度不确定、对象导数不确定时,就要用于递归。

递归复用

递归是把问题分解为一系列相似的小问题,通过函数不断调用自身来解决一个个小问题,直到满足结束条件,就完成了问题的求解。

TypeScript 类型系统不支持循环,但支持递归。当处理数据(个数、长度、层数)不固定的类型时,可以只处理一个类型,然后递归的调用自身处理下一个类型,直到结束条件也就是所有类型都处理完了,就完成了不确定数量的类型编程,达到循环的效果。

数组类型的递归

  1. // DeepPromiseValueType
  2. type ttt = Promise<Promise<Promise<Record<string, any>>>>;
  3. type DeepPromiseValueType<P extends Promise<unknown>> =
  4. P extends Promise<infer ValueType>
  5. ? ValueType extends Promise<unknown>
  6. ? DeepPromiseValueType<ValueType>
  7. : ValueType
  8. : never;
// ReverseArr [1, 2, 3, 4, 5] -> [5, 4, 3, 2, 1]
type ReverseArr<Arr extends unknown[]> =
    Arr extends [infer First, ...infer Rest]
            ? [...ReverseArr<Rest>, First]
            : Arr;

type IsEqual<A, B> = (A extends B ? true : false) & (B extends A ? true : false);

// Includes [1, 2, 3, 4, 5] includes(4) -> true
type Includes<Arr extends unknown[], FindItem> =
    Arr extends [infer First, ...infer Rest]
            ? IsEqual<First, FindItem> extends true
                    ? true
                    : Includes<Rest, FindItem>
            : false;

// RemoveItem [1, 2, 2, 3], removeItem(2) -> [1, 3]
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;

// BuildArry BuildArray<5> -> [unknown, unknown, unknown, unknown, unknown]
type BuildArray<
      Length extends number,
      Ele = unknown,
      Arr extends unknown[] = []
> = Arr['length'] extends Length
                ? Arr
                : BuildArray<Length, Ele, [...Arr, Ele]>;

字符串类型的递归

// ReplaceAll
type ReplaceAll<
      Str extends string,
      From extends string,
      To extends
> = Str extends `${infer Left}${From}${infer Right}`
                ? `${Left}${To}${ReplaceAll<Right, From, To>}`
                : Str;

// StringToUnion 'abc' -> 'a' | 'b' | 'c'
type StringToUnion<Str extends string> = 
        Str extends `${infer First}${infer Rest}`
                ? First | StringToUnion<Rest>
                : never;

// ReverseStr
type ReverseStr<
      Str extends string,
      Result extends string = ''
> = Str extends `${infer First}${infer Rest}`
        ? ReverseStr<Rest, `${First}${Result}`>
        : Result;

对象类型的递归

// DeepReadonly
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];
}