Types and inference

Use a tuple for fixed positions

Describe an array whose positions have known meanings and possibly different types.

An array type says every element looks the same. Sometimes that is not what you have. A coordinate pair holds exactly two numbers, and the order matters: the first is x, the second is y. A tuple describes an array where each position has a known meaning and type:

type Point = [number, number]
const origin: Point = [0, 0]

TypeScript now rejects a missing position, an extra position in the literal, or a string where a number belongs. Try const bad: Point = [0] and you get:

error TS2322: Type '[number]' is not assignable to type 'Point'.
  Source has 1 element(s) but target requires 2.

Reads respect positions too. origin[0] is a number. origin[2] is an error, because the tuple has no third element:

error TS2493: Tuple type 'Point' of length '2' has no element at index '2'.

Remember the rule from the first module. Tuples are plain JavaScript arrays at runtime. The position rules exist only during checking. Array.isArray(origin) returns true, and the emitted JavaScript is just [0, 0].

Labeled positions

You can give positions names, for editor help:

type Point = [x: number, y: number]

The labels change nothing at runtime. The value is still [0, 0]. But hover over a function that accepts a Point and you see x and y instead of two anonymous numbers. Call sites get easier to read.

Tuples and destructuring

Tuples shine as return values, because destructuring gives each position a real name:

function divide(a: number, b: number): [result: number, remainder: number] {
  return [Math.floor(a / b), a % b]
}

const [result, remainder] = divide(17, 5)
// result: 4, remainder: 2

Both variables are inferred as number, no annotation needed. You have seen this convention before if you use React: useState returns a [value, setter] tuple.

Tuple or object?

Use a tuple for compact pairs and for return conventions people already know. Use an object when readers benefit from names at the call site:

type Point = { x: number; y: number }

With three or more positions, point[2] tells the reader nothing, while point.z explains itself. My rule: two elements with an obvious order, tuple. Anything more, object.

Try this on your own: write a function that returns [value: string, found: boolean]. Destructure the result and hover over both variables to see the inferred types.

Lesson completed