In TS it is quite simple to write functions which will shift and unshift elements from the tuple. Consider:
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']
But you have ask about other way round, this is more tricky but by using above Shift and Unshift its doable. Consider:
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']
Full code is available in the playground.
Both types are based on recursion types, for example taking the first type Append:
ReducedT is an array which we will use to take out elements from the original Array
Result - this is our final array, we put things to it (pay attention we start from [A] so from element we want to have last
Rest - it represents next iteration of ReducedT so we shift one element out
Last - last element from original array
Rest['length'] extends 0 ? Unshift<Last, Result> : Append<T, A, Rest, Unshift<Last, Result>> - every iteration checks if our reduced array is not empty, if is we do last unshift and end the recursion, if its not empty we Append again with smaller ReducedT and bigger Result as we shift from one and unshift to the second.
[K in keyof ReducedT] is here only to trick TS type system, as without it TS would complain about infinite call