4

Why this produces an error "A rest element type must be an array type."?

type QWE<T extends [number, string]> = [boolean, ...T]
                                                 ^^^^ the error is here
Nurbol Alpysbayev
  • 19,522
  • 3
  • 54
  • 89
  • I'm guessing it has to do with the generic nature of `T`, since even `T extends any[]` emits the same error. – Madara's Ghost Dec 31 '18 at 08:16
  • 1
    Before 3.0 we could not spread tuples or generic type arguments to a function and this had to be implemented. Similarly, rest in tuples currently only support arrays, nobody implemented the ability to spread other tuples into the rest of a given tuple – Titian Cernicova-Dragomir Dec 31 '18 at 08:27
  • @TitianCernicova-Dragomir I just wanted to ask you to answer, but distracted to that answer about `never` ) Anyway, so there is no way to somehow modify tuples? No way to add something to the beginning or to the ending of a tuple? P.S. I would be happy to say my thanks as upvotes and selecting your answer – Nurbol Alpysbayev Dec 31 '18 at 08:30
  • At the beginning can be done by spreading the tuple to a function and extracting the arguments, I can provide it in an answer if you want. At the end I think there are some unrecommended hacks that involve recursive type aliases but they are explicitly not recomended – Titian Cernicova-Dragomir Dec 31 '18 at 08:32
  • @TitianCernicova-Dragomir Yes, of course I want! Especially because I didn't quite understand the approach you wrote about :) Thanks in advance! – Nurbol Alpysbayev Dec 31 '18 at 08:33

3 Answers3

6

Before 3.0 we could not spread tuples or generic type arguments to a function and this had to be implemented. Similarly, rest in tuples currently only support arrays, nobody implemented the ability to spread other tuples into the rest of a given tuple, and I'm guessing implementing it would require significant effort and complexity.

To add a tuple at the end of another known tuple we can use the ability to spread a tuple tu a function and then extract the argument types as a tuple.

type ArgumentTypes<T extends (...a: any) => any> =
    T extends (...a: infer A) => any ? A : never;
type QWE<T extends [number, string]> = 
    ArgumentTypes<(a: boolean, ...r: T) => void>

type R = QWE<[number, string]>

Adding the tuple at the start is more problematic, I believe there are some very unrecommended hacks that can achieve this using recursive type aliases. You can also define multiple conditions to support up to a number of elements in a tuple, but I would avoid it if possible

Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357
6

If someone can find it useful, this is a version of Titian's type, with an improvement covering some more cases:

type R = TupleUnshift<boolean, [number, string]> // [boolean, number, string]


type TupleUnshift<A, B extends [...any[]]> = ((a: A, ...r: ForcedTuple<B>) => void) extends (...a: infer R) => any ? R : never

type ForcedTuple<T> =
    T extends [
        infer A,
        infer B,
        infer C,
        infer D,
        infer E,
        infer F,
        infer G,
        infer H,
        infer I,
        infer J,
        infer K,
        infer L,
        infer M,
        infer N,
        infer O,
        infer P,
        infer Q,
        infer R,
        infer S,
        infer T,
        infer U,
        infer V,
        infer W,
        infer X,
        infer Y,
        infer Z
    ]
    ?
    [A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z]
    : T
Nurbol Alpysbayev
  • 19,522
  • 3
  • 54
  • 89
6

With TypeScript 4.0 variadic tuple types, adding an item I to a tuple type T gets much easier:

type Prepend<I, T extends unknown[]> = [I, ...T]
type Append<I, T extends unknown[]> = [...T, I]
type AddBetween<I, T extends unknown[], U extends unknown[]> = [...T, I, ...U]

type PrependTest = Prepend<boolean, [string, number]> 
// [boolean, string, number]

type AppendTest = Append<boolean, [string, number]> 
// [string, number, boolean]

type AddBetweenTest = AddBetween<boolean, [string], [number]> 
// [string, boolean, number]

Playground sample code

ford04
  • 66,267
  • 20
  • 199
  • 171
  • great type, too bad that after using it parameters names disappear – ElSajko Mar 29 '21 at 13:24
  • 1
    @ElSajko you can adjust above types to deal with tuple-only parameter types, so that labeled tuples elements can provide parameter names. E.g. `type Append = [...T, ...I]`; `type T1 = Append<[qux: string], Param>`, see [example](https://www.typescriptlang.org/play?#code/C4TwDgpgBAgmkDsAmAeAklCAPYFkGcoBXBAawQHsB3BAbQF0AaKAFUxzyUJPOrvoB8UALxRaAOkktmk8WnpQAsACgVAMxIBjYAEsKCKGooUAFACMAhgCcAXFHzArOhAHNmlgF52ERALZmIKwBKKABvAF8VUEgoAAVrC18ROITfCFwrfBRoiAo1Q2MBKPBoFgBGZLhEVFoARyIsOwcnVyYUq0SBIA). – ford04 Mar 30 '21 at 14:49