Course outline · 0% complete

0/26 lessons0%

Course overview →

Tuples: Arrays with Fixed Positions

lesson 2-4 · ~10 min · 7/26

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.

[string, number] — a tuple slot 0 string slot 1 number "score" 42 (string | number)[] — a plain array any of both any of both any length no position rules
A tuple fixes the length and gives each slot its own type, so position 0 and position 1 are checked separately.

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] = point is an error. The tuple has no third slot, and the compiler says so rather than handing you an undefined.
  • 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 if statements live in one for...of loop over nums, so the array is walked a single time.
  • Starting min and max at nums[0] matters. Starting min at 0 would report 0 for 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 typed number from its position.