对于提取或构造的数组元素个数不确定、字符串长度不确定、对象导数不确定时,就要用于递归。
递归复用
递归是把问题分解为一系列相似的小问题,通过函数不断调用自身来解决一个个小问题,直到满足结束条件,就完成了问题的求解。
TypeScript 类型系统不支持循环,但支持递归。当处理数据(个数、长度、层数)不固定的类型时,可以只处理一个类型,然后递归的调用自身处理下一个类型,直到结束条件也就是所有类型都处理完了,就完成了不确定数量的类型编程,达到循环的效果。
数组类型的递归
// DeepPromiseValueType
type ttt = Promise<Promise<Promise<Record<string, any>>>>;
type DeepPromiseValueType<P extends Promise<unknown>> =
P extends Promise<infer ValueType>
? ValueType extends Promise<unknown>
? DeepPromiseValueType<ValueType>
: ValueType
: 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];
}