Skip to content

递归循环

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

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

数组

反转数组,因为数组长度不确定,所以每次只处理一个类型,剩余递归做

typescript
type ReverseArr<Arr extends unknown[]> = 
    Arr extends [infer First, ...infer Rest] 
        ? [...ReverseArr<Rest>, First] 
        : Arr

type Result = ReverseArr<[1, 2, 3, 4, 5]> // [5, 4, 3, 2, 1]

递归查找元素,比如查找数组是否有某一个元素

typescript
type Equal<A, B> = (A extends B ? true : false) & (B extends A ? true : false)

type Includes<Arr extends unknown[], Item> = 
    Arr extends [infer First, ...infer Rest]
        ? Equal<First, Item> extends true
            ? true
            : Includes<Rest, FindItem>
        : false

构造长度为 Length 的数组,每次判断下 Arr 的长度是否到了 Length,是的话就返回 Arr,否则在 Arr 上加一个元素,然后递归构造

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

type Result = BuildArray<3> // [unknown, unknown, unknown]

字符串

xx_xx 转成驼峰式写法

typescript
type camelCase<S extends string> = 
    S extends `${infer Prefix}_${infer C}${infer Rest}`
        ? `${Prefix}${Uppercase<C>}${camelCase<Rest>}`
        : S

索引

当对象数量(层数)不确定,就要想到递归

比如给有多层的对象,每层都加上 readonly

typescript
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]
}

测试一下

typescript
type Obj = {
    a: {
        b: {
            c: {
                d: () => void
                e: {
                    f: 'a'
                }
            }
        }
    }
}

type Result = DeepReadonly<Obj>

// type Result = {
//     readonly a: DeepReadonly<{
//         b: {
//             c: {
//                 d: () => void;
//                 e: {
//                     f: 'a';
//                 };
//             };
//         };
//     }>;
// }

如果取下一层

typescript
type Result = DeepReadonly<Obj>['a']

// type Result = {
//     readonly b: DeepReadonly<{
//         c: {
//             d: () => void;
//             e: {
//                 f: 'a';
//             };
//         };
//     }>;
// }

可以看到取的时候是有计算,但下层还是 DeepReadonly<xx>,这是因为 ts 的类型只有被用到的时候才会做计算

所以可以在前面加上一段 Obj extends never ? never 或者 Obj extends any 等,从而触发计算

typescript
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
typescript
type Result = DeepReadonly<Obj>

// type Result = {
//     readonly a: {
//         readonly b: {
//             readonly c: {
//                 readonly d: () => void;
//                 readonly e: {
//                     readonly f: 'a';
//                 };
//             };
//         };
//     };
// }