A tuple type fixes both the length and the type of each position, unlike a regular array where every element shares one type and the length is unconstrained. Elements can be named purely for documentation and editor tooltips (with zero effect on the actual type checking), positions can be marked optional with ?, and a rest element (...T[]) allows a variable-length tail after a fixed prefix.
Named tuple elements: documentation, not enforcement
These names show up in editor tooltips and error messages, making a tuple's meaning far clearer at a glance — but they don't change the underlying type checking at all, which is purely positional.
type Coordinate = [x: number, y: number]; // named — same runtime behavior as [number, number]
function distance(a: Coordinate, b: Coordinate): number {
return Math.sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2);
}
const p1: Coordinate = [0, 0];
const p2: Coordinate = [3, 4];
distance(p1, p2); // 5Optional elements and rest elements
? marks a trailing position as optional; a rest element (must come last) allows any number of additional elements of a given type after the fixed positions.
type Point = [x: number, y: number, z?: number]; // z is optional
const p2d: Point = [1, 2]; // fine — z omitted
const p3d: Point = [1, 2, 3]; // also fine
type StringsThenNumbers = [string, ...number[]]; // exactly 1 string, then any number of numbers
const s: StringsThenNumbers = ["total", 1, 2, 3];readonly tuples
readonly on a tuple type blocks both mutation of individual elements and array-mutating methods like push, purely at the type level — useful for a value meant to be a fixed, immutable coordinate or record.
function origin(): readonly [number, number] {
return [0, 0];
}
const o = origin();
// o[0] = 5; // COMPILE ERROR: cannot assign to a readonly tuple element
// o.push(1); // COMPILE ERROR: push doesn't exist on a readonly tuple