递归复用做循环

Promise的递归调用

提取不确定层数的Promise中的value类型的高级类型

type DeepPromiseValueType<P extends Promise<unknown>> = 
P extends Promise<infer ValueType> ?
  ValueType extends Promise<unknown> ?
    DeepPromiseValueType<ValueType>
  : ValueType
: never
type promise = DeepPromiseValueType<Promise<Promise<Record<string,any>>>>;

上面的代码可以简化为

type DeepPromiseValueType<T> = 
T extends Promise<infer ValueType> ?
  DeepPromiseValueType<ValueType>
: T
type promise = DeepPromiseValueType<Promise<Promise<Record<string,any>>>>;

数组类型的递归

确定长度的元组反转

type ReverseArr<Arr extends unknown[]> =
Arr  extends [infer One, infer Two, infer Three, infer Four, infer Five] ?
  [Five, Four, Three, Two, One]
: never;
type arr = ReverseArr<[1,2,3,4,5]>;

不确定长度的元组反转

type ReverseArr<Arr extends unknown[]> =
Arr  extends [infer First, ...infer Rest] ?
  [...ReverseArr<Rest>,First]
: Arr;
type arr = ReverseArr<[1,2,3,4,5]>;

寻找元组中是否存在某个元素

type Includes<Arr extends unknown[],FindItem> =
Arr extends [infer First, ...infer Rest] ?
  IsEqual<First,FindItem> extends true ? true : Includes<Rest, FindItem>
: false
type IsEqual<A,B> = (A extends B ? true : false) & (B extends A ? true : false);
type arr = Includes<[1,2,3,4],4>;

删除元组中的某个元素

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);
type arr = RemoveItem<[1,2,3,4,5],4>;

构建一个自定义长度的同一类别的数组

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

字符串类型的递归

将字符串中的所用的指定字串替换成另一个字符串

type Replace<
  Str extends string,
  From extends string,
  To extends string,
> = Str extends `${infer Left}${From}${infer Right}` ?
    Replace<`${Left}${To}${Right}`,From,To> :
    Str;
type str = Replace<'abc abc aaa abc aaa abc ab abc','abc',''>;

将字符串的每个元素提取出来转换成联合类型

例如将"123"转换成"1" | "2" | "3"

type StringToUnion<Str extends string> = Str extends `${infer Fitsr}${infer Rest}` ?
Fitsr | StringToUnion<Rest> :
never;
type str = StringToUnion<'123'>;

反转字符串类型

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

对象类型的递归(索引类型的递归)

递归将所有层的索引都变成只读

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]
}
type obj = DeepReadonly<{
  a: {
    b: 1
  }
}>

如果按照我们的想法,会写出上面的代码,但是如果测试一下我们就会发现,递归没有进行下去。因为ts的类型只有被用到的时候才会被计算。这里只是给第一次的索引加上了只读,但是没有使用。所以不会进行计算,我们可以加一个Obj extends any让其计算。

type DeepReadonly<Obj extends Record<string,any>> = Obj extends any ?
{
  readonly [Key in keyof Obj]: Obj[Key] extends Object ?
    Obj[Key] extends Function ?
      Obj[Key]
    : DeepReadonly<Obj[Key]>
  : Obj[Key]
} : never;
type obj = DeepReadonly<{
  a: {
    b: 1
  }
}>

参考资料

[1]TypeScript 类型体操通关秘籍