How to use generic rest element with tuple types?
Asked Answered
B

1

8

TypeScript is giving me an error with a generic tuples rest element

type Tuple<Value extends any[]> = [...Value];

Error:

$ tsc
src/index.ts:1:36 - error TS2574: A rest element type must be an array type.

1 type Tuple<Value extends any[]> = [...Value];

What am I missing here?


type Tuple<HEAD, TAIL extends any[]> = [HEAD, ...TAIL];

doesn't work, although this works:

type Tuple<Value extends any[]> = [...any[]];
Belldame answered 17/11, 2018 at 23:46 Comment(6)
What are you trying to accomplish here? How is [...Value] different from Value?Brodench
I was trying to make a Head and Tail generic types, but they don't work neither, so this should work in order for the rest to work, I'll update with what I was trying to accomplishMuscular
You can't use the spread operator on a type.Unspent
why ?, it works here function tuple<T extends any[]>(...arg: T) {}Muscular
There you are not using the spread operator on a type, but on an argument of type T.Unspent
@DenisFrezzato the type [string, ...number[]] is valid.Brodench
B
19

UPDATE for TS4.0+. TypeScript 4.0 introduced support for variadic tuple types, so the code in this question should work as written, without needing to reword your operation in terms of function parameters. Hooray!


TS3.9- ANSWER:

The only thing you're missing is that such inference isn't currently supported. Maybe it will be in a future version of TypeScript. Luckily there is still a way to do what you want by expressing the desired operation in terms of one on function parameters, since as of TypeScript 3.0, there is a way to convert between tuple/array types and function parameters, and you can use infer as desired on parameter types.

Here's one possible implementation of Tail:

type Tail<T extends any[]> = 
  ((...t: T) => void) extends ((x: any, ...u: infer U) => void) ? U : never;

type TestTail = Tail<[1,2,3,4]>; // [2,3,4]

Notice that you take the tuple/array type T, spread it to a parameter list, then infer everything after the first parameter as another tuple/array rest type.

Similarly, you can implement your Tuple in a way that I'd call Cons:

type Cons<H, T extends any[]> = 
  ((h: H, ...t: T) => void) extends ((...u: infer U) => void) ? U : never;

type TestCons = Cons<string, [number, boolean]>; // [string, number, boolean]

And I'll leave Head as an exercise for you (that's what people say when they want to seem smart instead of lazy).

Anyway, hope that helps you make progress. Good luck!

Brodench answered 18/11, 2018 at 0:5 Comment(1)
thanks, I was hopping I could do something like type Tuple<Head, Tail extends any[]> = [Head, ...Tail]; type Head<T> = T extends Tuple<infer H, any[]> ? H : never; type Tail<T> = T extends Tuple<any, R> ? R : never; , but oh wellMuscular

© 2022 - 2024 — McMap. All rights reserved.