主题
递归循环
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';
// };
// };
// };
// };
// }