When each position means something
number[] says every element is a number, but nothing about how many elements there are or what each position means. Some data is positional by nature. A 2D point is exactly two numbers, x then y. A lookup result is a name and then a score. With a plain array type, swapping the two or adding a third slips through silently, because the array type has no opinion about positions.
A tuple type fixes the length and gives each position its own type:
const point: [number, number] = [3, 7]; const entry: [string, number] = ["score", 42];
The compiler now enforces position: entry[0] is a string, entry[1] is a number, and entry[2] is an error because the tuple has exactly two slots.
Compare (string | number)[], which allows any mix, any order, and any length. The tuple is the stronger and more honest claim, and it is how a function can return two values at once with full checking.
Tuples pair naturally with destructuring. Writing const [x, y] = point; declares x and y and fills them from positions 0 and 1, and each variable gets its positional type, so here both are number.
Reading a tuple by position and by destructuring
const point: [number, number] = [3, 7]; const entry: [string, number] = ["score", 42]; const [x, y] = point; console.log("x=" + x + " y=" + y); console.log(entry[0] + " -> " + entry[1]);
Output
x=3 y=7 score -> 42
The destructuring line unpacks point by position, so x and y are both typed number.
Where the position types show up
entry[0].toUpperCase()compiles because position 0 is a string.entry[1].toUpperCase()does not compile, because position 1 is a number. Same variable, different rules per slot.- Writing
const [a, b, c] = pointis an error. The tuple has no third slot, and the compiler says so rather than handing you anundefined. - Both reads above are ordinary bracket access at runtime. Tuples are plain JavaScript arrays once the types are erased, so there is no new data structure to learn.
Why a swapped tuple fails to compile
const user: [string, number] = [36, "Ada"] is rejected because the positions are swapped: slot 0 must be a string and slot 1 must be a number.
A tuple types each position individually. The literal [36, "Ada"] puts a number where the string slot is and a string where the number slot is, so both positions fail and the compiler reports two errors rather than one.
A looser (string | number)[] would have accepted either order without complaint, and that looseness is exactly what tuples exist to remove. The fix is to write the values in the declared order, as ["Ada", 36].
minMax: returning two values as one tuple
minMax(nums: number[]): [number, number] finds the smallest and largest value in one pass and returns both.
function minMax(nums: number[]): [number, number] { let min = nums[0]; let max = nums[0]; for (const n of nums) { if (n < min) min = n; if (n > max) max = n; } return [min, max]; } const [low, high] = minMax([7, 2, 9, 4]); console.log("low " + low + ", high " + high);
Output
low 2, high 9
Reading the code
- The return type
[number, number]promises exactly two numbers in a fixed order, smallest first. A caller reading the signature knows which is which without opening the body. - Both
ifstatements live in onefor...ofloop overnums, so the array is walked a single time. - Starting
minandmaxatnums[0]matters. Startingminat0would report0for an array of all-positive numbers, which is a classic off-by-assumption bug. - The result is unpacked with
const [low, high] = minMax([7, 2, 9, 4]);, and each variable is typednumberfrom its position.