ESC

Type to search the knowledge base.

Tuple Types

Fixed-length arrays with per-position types — useState pairs, CSV rows, rest parameters, and labeled tuples in TypeScript.

intermediate3 min read
  • typescript
  • tuple-types

A tuple is an array type with a known length and a type per index:

type Pair = [string, number];
const entry: Pair = ['retries', 3];

Unlike string[], position 0 is not interchangeable with position 1.

Docs: Tuple Types, Variadic tuple types.

Why tuples exist

// Weak
function useThing(): (string | number)[] {
  return ['count', 0];
}

// Strong
function useThing(): [string, number] {
  return ['count', 0];
}

const [label, value] = useThing();
// label: string, value: number

React’s useState return type is essentially a tuple: [state, setter].

Labeled tuples (readability)

type Range = [start: number, end: number];

function slice(range: Range) {
  const [start, end] = range;
  return { start, end };
}

Labels are documentation for the type system — they don’t create runtime fields.

Optional and rest elements

type CssRule = [property: string, value: string, important?: boolean];

type StringBooleans = [string, ...boolean[]];

Variadic tuples power typed compose and middleware helpers:

type Head<T extends unknown[]> = T extends [infer H, ...unknown[]] ? H : never;
type Tail<T extends unknown[]> = T extends [unknown, ...infer R] ? R : never;

as const produces readonly tuples

const point = [10, 20] as const;
// readonly [10, 20]

function move(p: readonly [number, number]) {
  return [p[0] + 1, p[1] + 1] as const;
}

Open-ended lists vs fixed pairs

Use Type
List of tags string[]
Key/value pair [string, string]
RGB [number, number, number]
React state slot [T, Dispatch<…>]

If length varies freely, use an array. If positions mean different things, use a tuple.

Destructuring parameters

function distance([x1, y1]: [number, number], [x2, y2]: [number, number]) {
  return Math.hypot(x2 - x1, y2 - y1);
}

Readonly tuples

type ImmutablePair = readonly [string, number];

Prevents push and index assignment through the type.

Footguns

  1. Assignability to arrays — tuples are special arrays; excess length rules can surprise.
  2. [string] vs string[] — one-element tuple is not a list.
  3. Inference without as const — [1, 2] may infer number[].
  4. Empty tuple [] — means empty; useful for “no args”.
const t: [number, number] = [1, 2];
const arr: number[] = t; // ok
// const t2: [number, number] = arr; // error — length unknown

Interview out-loud answer

“Tuples fix length and per-index types — good for pairs and coordinates. Arrays are for homogeneous open lists. I use labeled tuples for clarity and as const when I need literal readonly tuples.”

Variadic example

function concat<T extends unknown[], U extends unknown[]>(a: [...T], b: [...U]): [...T, ...U] {
  return [...a, ...b];
}
const r = concat([1, 2] as const, ['a'] as const); // [1, 2, 'a']

Variadic tuples power typed middleware pipelines and Promise.all style helpers in libraries.

Extra practice

Write a minimal demo in a scratch file or the playground: one happy path, one failure path, and one boundary input. If you cannot exhibit a bug that the pattern prevents, you do not own the concept yet — re-read the primary docs linked below and tighten the example until the failure is obvious.

Further reading

Related guides