Arrays and tuples

TypeScript · Essentials · 10 min

Lists are everywhere, and tuples are how TypeScript types functions that return several values, like React's useState.

An array type is written T[] or Array<T> — both mean the same thing. Mixed arrays use a union in parentheses: (string | number)[]. Array methods are fully typed, so nums.map(n => n.toFixed(1)) returns string[].

A tuple is an array with a fixed length and a known type at each position: [string, number]. Tuples are perfect for small, positional results — a key/value pair, a coordinate, or a function returning two things:

function useCounter(): [number, () => void] { /* ... */ }
const [count, increment] = useCounter();

Tuples can have labels for documentation ([x: number, y: number]), optional elements ([string, number?]) and rest elements ([string, ...number[]]).

Note that an array literal is inferred as an array, not a tuple: const p = [1, "a"] is (string | number)[]. Annotate the tuple type, or use as const (covered later) to get one.

`readonly` arrays (readonly string[] or ReadonlyArray<string>) remove mutating methods like push and sort. Use them for parameters you don't intend to change; callers can still pass ordinary arrays. The reverse is not allowed — a readonly array can't be passed where a mutable one is expected.

One important hole: by default, indexing arr[10] is typed as the element type even if the array is shorter, so the result could really be undefined. The noUncheckedIndexedAccess option (see the strict-options lesson) makes such reads T | undefined.

Example

const scores: number[] = [90, 72, 85];
const labels: Array<string> = scores.map(s => `${s}%`);

// A tuple: fixed positions with known types
type Entry = [key: string, value: number];
const entry: Entry = ["apples", 3];
const [fruit, qty] = entry;          // fruit: string, qty: number

// Optional and rest elements
type Log = [message: string, ...details: unknown[]];
const log: Log = ["saved", { id: 1 }, 42];

function total(values: readonly number[]): number {
  // @ts-expect-error -- push does not exist on readonly number[]
  values.push(0);
  return values.reduce((a, b) => a + b, 0);
}

console.log(labels, fruit, qty, log.length, total(scores));

Remember

  • T[] and Array<T> are the same type
  • Tuples type fixed-length positional data: [string, number]
  • Take readonly T[] parameters when you don't mutate the input

Watch out for

  • Expecting const pair = [1, "a"] to be a tuple — it's inferred as (string | number)[].

Check yourself

  1. What type is inferred for const pair = ["x", 1]?
  2. Can you pass a readonly number[] to a function whose parameter is number[]?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.typescriptlang.org/docs/handbook/2/objects.html#tuple-types