Tuples
A tuple is an array with a fixed number of elements where each position has its own type. It's the right tool when position carries meaning — a coordinate pair, a key and its value, or a function that returns two things at once.
Declaring a tuple
let point: [number, number] = [10, 20]; let entry: [string, number] = ["apples", 5]; point = [10, 20, 30]; entry = [5, "apples"];
error TS2322: Type '[number, number, number]' is not assignable to type '[number, number]'. error TS2322: Type 'number' is not assignable to type 'string'.
[string, number] means exactly two elements: a string, then a number. Both the count and the order are checked, unlike (string | number)[] where any element may be either type and there can be any number of them.
Destructuring tuples
Tuples pair naturally with array destructuring, which gives each position a name:
function minMax(values: number[]): [number, number] { return [Math.min(...values), Math.max(...values)]; } const [low, high] = minMax([7, 2, 9, 4]); console.log(`range: ${low}–${high}`);
range: 2–9
This is the same pattern React's useState uses: it returns a [value, setter] tuple so you can name both pieces whatever you like.
Named (labelled) tuple elements
Labels make a tuple type self-documenting. They don't change how it behaves — they only show up in editor tooltips and error messages:
type Range = [start: number, end: number]; function length([start, end]: Range): number { return end - start; } console.log(length([3, 10]));
7
Optional and rest elements
A ? marks a trailing element as optional, and ... allows any number of elements of one type:
type RGB = [red: number, green: number, blue: number, alpha?: number]; type Command = [name: string, ...args: string[]]; const solid: RGB = [255, 128, 0]; const faded: RGB = [255, 128, 0, 0.5]; const cmd: Command = ["git", "commit", "-m", "init"]; console.log(solid.length, faded.length); console.log(`${cmd[0]} has ${cmd.length - 1} args`);
3 4 git has 3 args
Readonly tuples
A plain tuple can still be changed through array methods — the length guarantee is only checked when you assign a whole tuple. Marking it readonly blocks mutation entirely:
const origin: readonly [number, number] = [0, 0]; origin[0] = 5;
error TS2540: Cannot assign to '0' because it is a read-only property.
Writing as const after an array literal (lesson 14) produces a readonly tuple of literal types automatically: [0, 0] as const is typed readonly [0, 0].
When to use a tuple vs an object
Tuples are compact, but positions have no names at the use site — user[2] says nothing about what's stored there. A good rule: use a tuple for two or three closely related values that are usually destructured immediately; use an object with named properties for anything larger or longer-lived.
Try it yourself
.push() on a non-readonly tuple compiles fine and quietly makes it longer than its type says — another reason to prefer readonly tuples when the shape matters.