Reputation: 451
Ok, so this is mostly for my curiosity. See below code:
const a: [string, number] = ["string", 1]; //valid
const b: Array<string, number> = ["string", 1]; //invalid
const c: Array<[string, number]> = ["string", 1] //invalid, it means [["string", 1]]
There seems to be no way to declare a tuple unless you use []
notation. My next thought was to look for a tuple type, but there seems to be nothing built in.
You could declare a Tuple
type like so: type Tuple<K, V> = [K, V]
but that defeats the whole idea.
Also something like :
let d: Array<string | number> = ["string", 1]
Would compile but you would be able to assign something like ["string", "another string", 1, 2, "stuff"]
to it.
Update 1:
As indicated by @jcalz type Tuple<K, V> = [K, V]
is incorrect in that it limits the number of elements of the tuple type to 2.
Upvotes: 1
Views: 161
Reputation: 329418
What exactly "defeats the whole idea" of type Tuple<K, V>
? Is it that Tuple<>
doesn't allow an arbitrary number of type parameters? Or that it uses []
inside its definition?
Here is one possible path to a solution, depending on your use case:
type Tuple<A, B = never, C = never, D = never, E = never, F = never, G = never> =
[G] extends [never] ? [F] extends [never] ? [E] extends [never] ?
[D] extends [never] ? [C] extends [never] ? [B] extends [never] ?
[A] : [A, B] : [A, B, C] : [A, B, C, D] : [A, B, C, D, E] :
[A, B, C, D, E, F] : [A, B, C, D, E, F, G]
Here, Tuple<>
is a conditional type which uses default generic parameters to allow you to specify anywhere between one and seven tuple elements:
const e: Tuple<string, number> = ["string", 1]; // okay
If you need more elements you can extend the above definition, although you can never get an arbitrary number of them without something like variadic kinds, which are not part of TypeScript (as of 2.9 anyway).
Or maybe this doesn't count because tuple syntax is being used inside the definition. If you want to completely avoid tuple syntax, you could try to make a type which is similar to a tuple... something like this for the two-element case:
interface FakeTuple2<A, B> extends Array<A | B> {
readonly length: 2,
0: A,
1: B,
}
This is tuple-like enough for many purposes, depending on what you need:
const f: FakeTuple2<string, number> = ["string", 1]; // okay
And if you want a variable number of elements as well as the fake tuple, you could use both approaches above to get something like:
type StripNever<T> = Pick<T, { [K in keyof T]: T[K] extends never ? never : K }[keyof T]>
type FakeTuple<A, B = never, C = never, D = never, E = never, F = never, G = never> =
Array<A | B | C | D | E | F | G> &
StripNever<{ 0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G }> & {
length: [G] extends [never] ? [F] extends [never] ? [E] extends [never] ?
[D] extends [never] ? [C] extends [never] ? [B] extends [never] ?
1 : 2 : 3 : 4 : 5 : 6 : 7
}
which also works for one through seven elements and can be extended.
So those are some ideas for you. Hope they help. Good luck!
Upvotes: 3