[TypeScript] Variadic Tuple Types V4 ...T
Before V4, for type, it is possible to use ...spread[] as the last element of the tuple.
// Worked this way, even before TS 4.x
enum Sandwich {
Hamburger,
VeggieBurger,
GrilledCheese,
BLT
}
type SandwichOrder = [
number, // order total
Sandwich, // sandwich
...string[] // toppings
]
const order1: SandwichOrder = [12.99, Sandwich.Hamburger, "lettuce"]
const order2: SandwichOrder = [14.99, Sandwich.Hamburger, "avocado", "cheese"]
// Error: Type 'string' is not assignable to type 'Sandwich'.
const order_with_error: SandwichOrder = [
10.99,
"lettuce"
]
The same as generics for that spread type at the end of the tuple:
// Worked this way, even before TS 4.x
type MyTuple<T> = [number, ...T[]]
const x1: MyTuple<string> = [4, "hello"]
const x2: MyTuple<boolean> = [4, true]
It’s important to note that, before TS 4.0 we had to use ...T[]
What's the problem then?
enum Sandwich {
Hamburger,
VeggieBurger,
GrilledCheese,
BLT
}
type SandwichOrder = [
number, // order total
Sandwich, // sandwich
...string[] // toppings
]
const order1: SandwichOrder = [12.99, Sandwich.Hamburger, "lettuce"]
/**
* return an array containing everything except the first element
*/
function tail<T>(arg: readonly [number, ...T[]]) {
const [_ignored, ...rest] = arg
return rest
}
// const orderWithoutTotal: (string | Sandwich)[]
const orderWithoutTotal = tail(order1)
We expected `orderWithoutTotal` should be [Sandwich, ...string[]]. Instead of (string | Sandwich)[]
From V4, we are able to get correct type:
function tail<T extends any[]>(arg: readonly [number, ...T]) {
// add _ to _ignored, to stop Typescript unsed variable compalins
const [_ignored, ...rest] = arg;
return rest;
}
const order1: SandwichOrder = [12.99, Sandwich.Hamburger, "lettuce"]
// const result: [Sandwich, ...string[]]
const result = tail(order1)
We can also now use more than one ...spread
in a single tuple
type MyTuple = [
...[number, number],
...[string, string, string]
]
// const x: [number, number, string, string, string]
const x: MyTuple = [1,2,'a', 'b', 'c']
It’s important to note that only one ...rest[]
element is possible in a given tuple, but it doesn’t necessarily have to be the last element
// Good
type t1 = [...[number, number], ...string[]]
type t2 = [boolean, ...number[], string]
// Bad
type b1 = [...number[], ...string[]]