Arrays, Tuples and readonly

beginner
10 min

Arrays, Tuples and readonly

Collections are everywhere, and TypeScript gives you precise tools for them: arrays for homogeneous lists of any length, tuples for fixed-length sequences with a known type at each position, and readonly variants that prevent mutation. This lesson covers all three, plus practical patterns like as const.

Typing Arrays

There are two equivalent syntaxes:

typescript
const scores: number[] = [90, 85, 77]; const names: Array<string> = ["Ada", "Grace"];

Most style guides prefer number[] for simple element types. Arrays of complex types read well either way:

typescript
interface Todo { title: string; done: boolean } const todos: Todo[] = [ { title: "Learn tuples", done: false }, ];

Array methods stay fully typed: todos.filter(t => t.done) returns Todo[], and scores.map(s => s * 2) returns number[]. If you start from an empty array, annotate it — otherwise TypeScript infers never[] or a too-loose type:

typescript
const results: string[] = []; // annotate empty arrays

Arrays of Unions vs Unions of Arrays

Parentheses matter:

typescript
const mixed: (string | number)[] = ["a", 1, "b"]; // each element is string or number const oneKind: string[] | number[] = [1, 2, 3]; // the whole array is one kind

Tuples: Fixed Positions, Fixed Types

A tuple declares exactly which type lives at each index:

typescript
type Point = [number, number]; const origin: Point = [0, 0]; type HttpResponse = [status: number, body: string]; // labeled elements const res: HttpResponse = [200, "OK"]; const [status, body] = res; // destructuring keeps the types

Tuples power patterns like React's useState, which returns [value, setter]. Labels (status:, body:) are purely documentation but make signatures much clearer.

Tuples can include optional and rest elements:

typescript
type Range = [start: number, end?: number]; type Row = [id: number, ...cells: string[]];

readonly Arrays and Tuples

Prefix with readonly (or use ReadonlyArray<T>) to forbid mutation:

typescript
const weekdays: readonly string[] = ["Mon", "Tue", "Wed"]; weekdays.push("Sat"); // Error: push does not exist on readonly string[] weekdays[0] = "Sun"; // Error: index signature is readonly const first = weekdays[0]; // reading is fine

Accepting readonly T[] in function parameters is a great habit: it promises callers you will not mutate their data, and a normal T[] is always assignable to readonly T[] (but not the reverse).

typescript
function total(values: readonly number[]): number { return values.reduce((sum, v) => sum + v, 0); }

as const: Deeply Immutable Literals

The as const assertion freezes a literal's types: arrays become readonly tuples and strings become literal types:

typescript
const sizes = ["sm", "md", "lg"] as const; // type: readonly ["sm", "md", "lg"] type Size = (typeof sizes)[number]; // "sm" | "md" | "lg"

This one-liner — derive a union from a constant array — is one of the most useful tricks in TypeScript: a single source of truth for both runtime values and compile-time types.

Key Takeaways

  • Use T[] for lists of any length; annotate empty arrays explicitly.
  • (A | B)[] allows mixed elements; A[] | B[] means one kind of array.
  • Tuples fix the type at each position and support labels, optional, and rest elements.
  • readonly T[] prevents mutation and is ideal for function parameters.
  • as const turns literals into readonly tuples and literal unions.

Next lesson: Enums and Literal Types — named constants and exact-value types.

Arrays, Tuples and readonly - TypeScript | CodeYourCraft | CodeYourCraft