В 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>>
- каждая итерация проверяет, не является ли наш уменьшенный массив не пустым, если мы делаем последний unshift и завершаем рекурсию, если он не пустой, мы снова добавляем с меньшим ReducedT
и большим Result
при переходе от одного к другому и отмене смещения. [K in keyof ReducedT]
здесь только для того, чтобы обмануть систему типа TS, так как без этого TS будет жаловаться на бесконечный вызов ?