25

Is there a way without type hinting to create a tuple in Typescript.

If I simply do

const tuple = [1, 2];

the type of Tuple number[]

The closest I can get to a oneliner is

const tuple: [number, number] = [1, 2];

Am I missing something, or is this the only way?

5
  • your solution will not work with any of indirect changes of the Array, e.g. tuple.push(123) Commented Apr 9, 2018 at 9:25
  • @Jay tuple itself is a type , if you declare [number, number] then you can only push number type in the array. The purpose of declaring an array as tuple is restrict the types of known one or more array elements . Commented Apr 9, 2018 at 9:27
  • 2
    The only way I know is with a function, this answer is the closest to a solution stackoverflow.com/questions/48872328/… Commented Apr 9, 2018 at 9:44
  • 2
    Wops, meant this one stackoverflow.com/questions/48686849/… Commented Apr 9, 2018 at 9:48
  • Awesome answer there @TitianCernicova-Dragomir. Key take away is that typescript can't infer tuples :( Feel free to add this as answer here and I can accept it, given the question there is different to mine Commented Apr 9, 2018 at 9:58

6 Answers 6

24

With typescript 3.0, you can have your own utility function:

const tuple = <T extends any[]>(...args: T): T => args

And use it this way:

const tup = tuple(1, 2) // tup type is [number, number]
Sign up to request clarification or add additional context in comments.

2 Comments

This doesn't work for arrays that have already been created: tsplay.dev/w62avw
@ShaneCallanan The type information is already been lost after the initial array is constructed, typescript cannot magically get the array length back, which is required in tuples. The best typescript can do with the type information it know (string[]) is making [...string], which is effectively a string array
20

Typescript will not infer tuple types from array literals. You can specify the type explicitly, as you have, or you can create a helper function to make it a bit easier and still get some inference.

const tuple = <T extends [any] | any[]>(args: T): T => args
tuple(["A", "B"]) // [string, string]

Edit

Starting from 3.4 you can also use an as const assertion. This does have the advantage of not needing the extra function but it will generate a read-only tuple:

var t = [1, ''] as const;
t[0] = 1  //err

Starting from 3.0 you can also use tuples in rest parameter to infer tuples:

const tuple = <T extends any[]>(...args: T): T => args
tuple("A", "B") // [string, string]

5 Comments

thanks for the details. hopefully in future at least we can use variadic kinds to avoid explicitly declaring many functions
@Jay I dream of variadic kinds ;)
Is there a way to do as const assertion that return mutable tuple? Something like [Number()] as MutableConst which give [number] instead of readonly [number]
Just leaving the link here, in case someone is searching for official docs of T extends [any] | any[]...
<T extends [] | any[]> and <T extends [] | {}> are shorter and work too.
12

As of TypeScript 3.4, you can add simply as const at the end.

const tuple = [1, 2] as const;

Full credit to @bela53's answer, which has a better example and link to TS playground.

1 Comment

The behavior is not the expected one since it will prepend readonly keywords on every attribute. The other solutions are better.
4

TypeScript 4.0 has an additional way to implictly infer tuple types:

The type [...T], where T is an array-like type parameter, can conveniently be used to indicate a preference for inference of tuple types[:] (docs)

const tuple = <T extends unknown[]>(args: [...T]): T => args
tuple(["A", "B"]) // [string, string]

Playground

Comments

2

I am adding this answer for reference as I found it to be a pain that as const creates readonly tuples and that other techniques widen the types of the values

const tuple = <T extends any[]>(xs: readonly [...T]): T => xs as T;

it can be used in 2 ways:

const a = tuple(['foo', 10] as const)

a is of type ["foo", 10], is not readonly andtypeof a[number] is "foo" | 10


const b = tuple(['foo', 10]);

b is of type [string, number] and typeof b[number] is string | number

Comments

0

I would recommend defining a type for the tuple because it's more expressive.

type TreeHouse = [location: Location, name: string, capacity: number];

and then use

<TreeHouse>[new Location(…), "Treeston", 6]

From what I tried this tuple literal cannot use parameter names unfortunately.

Take care of precedence! <TreeHouse>(…) has not the same precedence like new TreeHouse(…).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.