TypeScript Array of Tuples TypeScript Array of Tuples typescript typescript

TypeScript Array of Tuples


type TypeOfOptions = typeof options[number] // (string | number)[]type IsTypeOfOptionsSubsetOfSomeTuple =   TypeOfOptions extends SomeTuple ? true : false // false is not assignable to SomeTuple

It meansTypeOfOptions is just more general type then SomeTuple and naturally cannot be used as SomeTuple, this is exactly why you do have the error. You want [string, number] you get by automatic inference (string | number)[], clearly not the same.

Why its more general because TS type inference works like that, we can change the behavior by assert our type to more specific by as or : or make value constructor which will do that.

Below few possible options how to change the initial inference:

// value constructorconst makeOptions = (...a: SomeTuple[]): SomeTuple[] => aconst options1 = makeOptions(    ['first', 1],    ['second', 2],) // a is SomeTuple[]// using :const options2: SomeTuple[] = [    ['first', 1],    ['second', 2],];// using asconst options3 = [    ['first', 1],    ['second', 2],] as SomeTuple[]foo(options1) // okfoo(options2) // okfoo(options3) // ok

Additional note about question in the comment. Generally type SomeTuple is a specification of type TypeOfOptions, it means that if you have value which is TypeOfOptions it can be also SomeTuple but does not must be. Lets check if such relation is true:

type IsSomeTupleSubsetOfTypeOfOptions = SomeTuple extends TypeOfOptions ? true : false // true

So yes SomeTuple is specific variant of TypeOfOptions. It means we can assert type of TypeOfOptions into SomeTuple saying we are narrowing to this specific case. But if we do that - TS will validate the value to the narrowed type right away, if its match, type assertion is successful.


By default ['first', 1] is an array with a string or a number, or one can say it's type is (string|number)[]. Note that an array does not specify it's length, nor does it specify which element is a string or which is a number. You can push an item with 3 or more values in it and TypeScript will be just fine with that.

In your second example (['first', 1]) it's an [string, number][]. Note that this differs from (string|number)[]. Now it knows it's an array with two elements, the first being a string, the second being a number. To be able to help figure TypeScript this out, an explicit declaration is required.

If it were to infer [string, number][] from the start, you wouldn't be able to add [123, 'foobar'], [123] or [] anymore. This would lead to more confusing errors as you would have to specify the type for each of those cases instead (fixing one issue, having other issues return). And if it would infer based on the entire file or codebase, compilation would take years for a complex program.


TypeScript infers the type of options to be (string | number)[][] — an array of arrays containing either strings or numbers. It does not infer [string, number][].

This is a case of needing to specify the type, as in your otherOptions:

const otherOptions: SomeTuple[] = [    ['first', 1],    ['second', 2],];

TypeScript inference is really good, but it doesn't handle this specific case for you. It would be too limiting on options for it to assume you won't do options.push([1, 2]) in other code.

In this case, even as const doesn't help, because then the array entries are too specific (you can't assign readonly ['first', 1] to a SomeTuple).