TypeScript类型体操挑战(二十二)

99 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 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了,从这里开始进行处理