Lists in TypeScript come in three flavors. Most code uses regular arrays, but tuples and readonly arrays solve real problems too.
Arrays — variable length, one type
Two equivalent syntaxes. Use whichever you prefer (T[] is more common).
const nums: number[] = [1, 2, 3];
const names: Array<string> = ["a", "b"];
nums.push(4); // ✅
nums.push("5"); // ❌ string not assignable to number
Tuples — fixed length, fixed types per position
A tuple is an array where each index has its own type and the length is known. Think of it like an unnamed record.
type RGB = [number, number, number];
const red: RGB = [255, 0, 0];
const pair: [string, number] = ["age", 27];
// pair[0] is string, pair[1] is number
The classic use case: returning multiple values from a function (like React’s useState).
function useCounter(initial: number): [number, () => void] {
let count = initial;
const inc = () => { count++; };
return [count, inc];
}
const [count, increment] = useCounter(0);
Labeled tuples — better readability
We can name each slot for documentation. Labels don’t affect the type, just IDE hints.
type Range = [start: number, end: number];
const r: Range = [0, 10]; // hovering shows 'start' and 'end'
Optional and rest in tuples
type Optional = [string, number?]; // second item is optional
type Rest = [string, ...number[]]; // string followed by any number of numbers
const a: Optional = ["x"]; // ✅
const b: Rest = ["tag", 1, 2, 3]; // ✅
readonly arrays — can’t mutate
Adding readonly (or using ReadonlyArray<T>) blocks push, pop, splice, index assignment — anything that mutates.
const fixed: readonly number[] = [1, 2, 3];
fixed.push(4); // ❌ Property 'push' does not exist on readonly number[]
fixed[0] = 99; // ❌ Index signature is readonly
Why bother? Function parameters mostly. We signal that we won’t mutate the caller’s array.
function sum(nums: readonly number[]): number {
// can't accidentally modify nums here
return nums.reduce((a, b) => a + b, 0);
}
readonly tuples
const point: readonly [number, number] = [1, 2];
point[0] = 5; // ❌
as const — make everything readonly literally
Slapping as const at the end gives us a deeply readonly tuple of literals. Great for constants.
const directions = ["up", "down", "left", "right"] as const;
// type: readonly ["up", "down", "left", "right"]
type Direction = typeof directions[number]; // "up" | "down" | "left" | "right"
When to use which
- Array
T[]— most code. Lists of homogeneous things. - Tuple
[A, B]— multiple return values, fixed pairs (coordinates, key-value). readonly— parameters we shouldn’t mutate, public constants.as const— derive a union of string literals from an array.
Interview soundbite
“Arrays are homogeneous and variable-length. Tuples are fixed-length with per-position types — great for returning multiple values. readonly prevents mutation, useful for function parameters and shared constants.”