TypeScript类型体操挑战(十九)

102 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

中等

Chunk(lodash.chunk)

挑战要求

在线示例

type Chunk<T extends any[], U extends number, A extends any[] = []> = 
  T extends [infer F, ...infer E] 
  ? A['length'] extends U 
      ? [A, ...Chunk<E, U, [F]>] 
      : Chunk<E, U, [...A, F]>
  : A extends [] ? [] : [A]


// 使用示例
type exp1 = Chunk<[], 1> // 结果为 []
type exp3 = Chunk<[1, 2, 3], 1> // 结果为 [[1], [2], [3]]

直接通过一个 case 来了解整个过程:

Expect<
  Equal<
    Chunk<[1, 2, 3], 1>, 
    [[1], [2], [3]]
  >
>

1. 代入到类型中:

type Chunk<[1, 2, 3], 1, []> = 
  [1, 2, 3] extends [infer F, ...infer E] // 条件成立
  ? 0 extends 1 	// 条件不成立
      ? [A, ...Chunk<E, U, [F]>] 
      // 会走到这,结果如下:
      : Chunk<[2, 3], 1, [1]>
  : A extends [] ? [] : [A]

2. 代入结果Chunk<[2, 3], 1, [1]>

type Chunk<[2, 3], 1, [1]> = 
  [2, 3] extends [infer F, ...infer E] // 条件成立
  ? 1 extends 1 	// 条件成立
      // 会走到这,结果如下:
      ? [[1], ...Chunk<[3], 1, [2]>] 
      : Chunk<E, U, [...A, F]>
  : A extends [] ? [] : [A]

3. 根据第 2 步的结果代入类型Chunk<[3], 1, [2]>

type Chunk<[3], 1, [2]> = 
  [3] extends [infer F, ...infer E] // 条件成立
  ? 1 extends 1 	// 条件成立
      // 会走到这,结果如下:
      ? [[2], ...Chunk<[], 1, [3]>]
      : Chunk<E, U, [...A, F]>
  : A extends [] ? [] : [A]

4. 根据第 3 步的结果代入类型Chunk<[], 1, [3]>]

type Chunk<[], 1, [3]> = 
  [] extends [infer F, ...infer E] // 条件不成立
  ? A['length'] extends U 
      ? [A, ...Chunk<E, U, [F]>] 
      : Chunk<E, U, [...A, F]>
  // 会走到这,结果为   
  : [3] extends [] ? [] : [[3]]

将所有结果进行代入:

// 第 3 步结果
[[2], ...[[3]]]
// 等于
[[2], [3]]

// 第 2 步结果
[[1], ...[[2], [3]]]
// 最终结果等于
[[1], [2], [3]]

我的解题思路如图:

Fill(Array.fill())

挑战要求

在线示例

// 创建长度为 N 的数组
type BuildArray<N extends number, T extends any[] = []> = T['length'] extends N ? T : BuildArray<N, [...T, 0]>;

// 给元组 T 添加一个元素,再返回长度
type AddOne<T extends number> = [...BuildArray<T>, 0]['length']

type Fill<
  T extends unknown[],
  N,
  Start extends number = 0,
  End extends number = T['length'],
  U extends unknown[] = [],
  > =
  T extends [infer First, ...infer Rest]
  ? 
    Start extends End 
    ? [...U, ...T]
    : U['length'] extends Start
      ? Fill<Rest, N, AddOne<Start> & number, End, [...U, N]>
      : Fill<Rest, N, Start, End, [...U, First]>
  : U


// 使用示例
Fill<[], 0> // []
Fill<[1, 2, 3], 0>	// [0, 0, 0]
Fill<[1, 2, 3], true, 1, 3>	// [1, true, true]

答案参考于解答区

  • 其中占位类型 U 是用来存储迭代中处理的元素,BuildArrayAddOne只是工具类型。
  • 当当前索引位于StartEnd之间时,则使用N进行填充,并且Start要加 1,再继续进行迭代。
  • U的长度始终不等于Start时,说明Start大于T的长度。

解题思路汇总成一张图: