首页 > 解决方案 > TypeScript - 将类型附加到元组类型的末尾

问题描述

是否可以编写一个将附加类型附加到给定元组类型的助手?

type A = Append<['a', 'b'], 'c'> // resulting in ['a', 'b', 'c']

我也有兴趣加入 2 元组类型:

type B = Join<['a', 'b'], ['c', 'd']> // resulting in ['a', 'b', 'c', 'd']

标签: typescripttypescript-generics

解决方案


在 TS 中,编写从元组中移位和取消移位元素的函数非常简单。考虑:

type Shift<T extends Array<any>> 
= ((...a: T) => any) extends ((a: any, ...result: infer Result) => any) 
? Result 
  : never;

type Unshift<A, T extends Array<any>> 
= ((a: A, ...b: T) => any) extends ((...result: infer Result) => any) 
? Result 
  : never;

type A = Shift<['a', 'b', 'c']> // A is ['b','c']
type B = Unshift<'a', A> // B is again ['a', 'b', 'c']

但是您已经询问了其他方式,这更棘手,但通过使用上面Shift的方法是Unshift可行的。考虑:

type Append<
  T extends any[]
  , A
  , ReducedT extends any[] = T
  , Result extends any[] = [A]
  , Rest extends any[] = Shift<ReducedT>
  , Last extends T[keyof T] = T[Rest['length']]
  > = {
  [K in keyof ReducedT]: Rest['length'] extends 0 ? Unshift<Last, Result> : Append<T, A, Rest, Unshift<Last, Result>> 
}[0]

type R = Append<['a', 'b', 'c'], 'd'>
// ['a', 'b', 'c', 'd']

type Merge<
  A extends any[]
  , B extends any[]
  , ReducedA extends any[] = A
  , Result extends any[] = B
  , Rest extends any[] = Shift<ReducedA>
  , Last extends A[keyof A] = A[Rest['length']]
  > = {
  [K in keyof ReducedA]: Rest['length'] extends 0 ? Unshift<Last, Result> : Merge<A, B, Rest, Unshift<Last, Result>> 
}[0]

type AB = Merge<['a', 'b', 'c'], ['d','e','f']>
// ['a', 'b', 'c', 'd', 'e','f']

完整的代码在操场上可用。

两种类型都基于递归类型,例如采用第一​​种类型Append

  • ReducedT是一个数组,我们将使用它从原始数组中取出元素
  • Result- 这是我们的最终数组,我们把东西放进去(注意我们从[A]我们想要最后的元素开始
  • Rest- 它代表下一次迭代,ReducedT所以我们将一个元素移出
  • Last- 原始数组的最后一个元素
  • Rest['length'] extends 0 ? Unshift<Last, Result> : Append<T, A, Rest, Unshift<Last, Result>>- 每次迭代检查我们的简化数组是否不为空,如果我们最后一次取消移位并结束递归,如果它不为空,我们再次追加ReducedT越来越大Result,因为我们从一个转移到第二个。
  • [K in keyof ReducedT]在这里只是为了欺骗 TS 类型系统,因为没有它 TS 会抱怨无限调用

推荐阅读