携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情
中等
MapTypes
interface MapType {
mapFrom: any;
mapTo: any;
}
type GetValue<T extends MapType, U> =
T extends T
? U extends T['mapFrom'] ? T['mapTo'] : never
: never;
type MapTypes<T, R extends MapType> = {
[P in keyof T]: [GetValue<R, T[P]>] extends [never]
? T[P]
: GetValue<R, T[P]>
}
GetValue中,T extends T是为了产生分布行为,这样将T的每个类型进行比对,从而获得用来转换的类型值- 为了正确捕获到
nerver值,所以把值变为元组来进行判断,也就是[GetValue<R, T[P]>] extends [never],之后正确设置对应的转换类型即可
我在解答区看到一个更好更简洁的答案,通过一个自定义泛型Type就让代码变得更加优雅了:
type GetMapToType<
T,
R,
// Type 已经正确拿到需要转换的类型值了
Type = R extends { mapFrom: T; mapTo: infer To } ? To : never
> = [Type] extends [never] ? T : Type // 根据条件返回转换的类型即可
type MapTypes<T, R> = {
[key in keyof T]: GetMapToType<T[key], R>
}
Construct Tuple
// 创建一个给定长度的元组
type ConstructTuple<L extends number, R extends unknown[] = []> =
R['length'] extends L
? R
: ConstructTuple<L, [unknown, ...R]>
// 示例:
type result = ConstructTuple<2> // expect to be [unknown, unkonwn]
- 首先需要定义一个泛型
R用来存储元素,默认值就是空数组 - 逻辑就是通过判断
R的长度是否符合L,从而通过递归的方式创建一个符合长度的元组
Number Range
type AddOne<T extends unknown[] = []> = [...T, T['length']];
type NumberRange<L, H, R extends number[] = []> =
R['length'] extends L
? L extends H
? AddOne<R>[number]
: NumberRange<AddOne<R>['length'], H, AddOne<R>>
: NumberRange<L, H, [...R, never]>;
// 示例:
type result = NumberRange<2 , 9> // 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
- 定义一个泛型
R用来存储每个数值 - 首先判断
R的长度-
等于
L,则判断L是否等于H- 是的话表示结束了,所以将
R再处理一下,通过索引访问类型就可以返回所有元素组成的联合类型了。如果不等于 - 不是的话,就将开始索引
L加 1,R也新增一个元素
- 是的话表示结束了,所以将
-
不等于
L,说明还没到开始索引的范围,所以要先往R中填充元素never,通过索引类型返回R的值时,索引类型会被忽略掉
-
画个图:
type ConstructTuple<L, R extends any[] = []> =
R['length'] extends L
? R
: ConstructTuple<L, [never, ...R]>
type AddOne<T extends unknown[] = []> = [...T, T['length']];
type NumberRange<L, H, R extends any[] = ConstructTuple<L>> =
R['length'] extends H
? AddOne<R>[number]
: NumberRange<AddOne<R>['length'], H, AddOne<R>>
- 主要就是改变了
R的初始值,这样就可以直接跳到开始索引L了,从这里开始进行处理