Tuples in TypeScript Explained

A tuple is an array with a fixed length where each position has its own type. Learn how tuples differ from arrays, when to use them, and how they catch out-of-bounds errors.

6 min read

TypeScript tuples are arrays with a known, fixed length where each position has its own type. An array of strings means "any number of strings." A tuple of two elements, a string followed by a number, means "exactly two elements: a string at index 0 and a number at index 1."

TypeScript tuples let you model small, position-based groups of data without defining a named interface or type alias for every one-off case. They are the type system's way of saying "this array has a specific shape at specific indexes."

Writing a tuple type

A tuple type is written with square brackets containing the type at each position, separated by commas. The order of types matters -- swapping the first and second type creates an entirely different tuple. Here is a two-element tuple where the first position is a string and the second is a number, along with how you read values from each position:

typescripttypescript
let pair: [string, number] = ["hello", 42];
 
let word = pair[0];
let count = pair[1];

At index 0, TypeScript expects a string value, and at index 1, it expects a number value. When you read those indices, TypeScript gives you precisely those types without any union or narrowing.

TypeScript infers word as a string and count as a number, so you can call string methods on the first element and number methods on the second immediately. No type guard is needed because the tuple guarantees what lives at each position.

This is the key difference from a union array, where you must narrow before using member-specific methods. Each index position in the tuple has its own independent type that stays fixed no matter how you access it.

The compiler also blocks assignments that put the wrong type at a position. Here the string and number values are swapped compared to the tuple's declared order, so both positions now hold the wrong type:

typescripttypescript
let pair: [string, number] = [42, "hello"];

TypeScript checks each position independently and reports both mismatches at once, one error for each position that does not match the declared type:

texttext
Type 'number' is not assignable to type 'string'.
Type 'string' is not assignable to type 'number'.

The error pinpoints both positions individually, so you can see exactly which index needs to change.

How tuples differ from arrays

A regular array has one element type repeated for every element and an unknown length. You can access any index and TypeScript assumes the element type is present, even if the array is shorter at runtime.

A tuple has per-position types and a known, fixed length. Accessing an index beyond the tuple's declared length is a compiler error. This catches out-of-bounds access that would silently return undefined in plain JavaScript.

typescripttypescript
let pair: [string, number] = ["hello", 42];
 
let c = pair[2];

Trying to access index 2 on a two-element tuple produces a compiler error instead of silently returning undefined at runtime. The compiler tells you exactly which tuple type is involved and which index is out of bounds:

texttext
Tuple type '[string, number]' of length '2' has no element at index '2'.

The length property of a tuple is also typed as a literal number, not the broad number type. For a two-element tuple, the length is typed as exactly 2. This means TypeScript can reason about the exact size of the tuple throughout your code.

Destructuring tuples

JavaScript array destructuring works with tuples and preserves the per-position types for each variable. Each destructured variable gets exactly the type from its source position, so there is no extra annotation to write.

typescripttypescript
let pair: [string, number] = ["hello", 42];
 
let [word, count] = pair;

After destructuring, word has type string and count has type number. You can call string methods on word and number methods on count without any additional checks.

This is why tuples pair naturally with patterns like React's useState, where the return type of state value and setter function is a two-element tuple. Destructuring that return value gives you a correctly typed state variable and a correctly typed setter function in one line.

Labeled tuples

TypeScript lets you add labels to tuple elements for documentation purposes. Labels do not change the type at all -- they are erased at compile time -- but they make the intent clearer in editor tooltips when you hover over an element.

typescripttypescript
type HttpResult = [status: number, body: string];
 
let result: HttpResult = [200, "OK"];

When you hover over result[0] in your editor, the tooltip shows "status: number" instead of just "number." The labels are purely for readability and have no effect on the compiled JavaScript or the type-checking behaviour.

Labels also make destructuring easier to review, since a reviewer can see status and body in the type definition instead of guessing what each position means from usage alone.

When to use tuples vs objects

Tuples work well when the position-based meaning is obvious and the group is small. Objects work better when property names are needed for clarity or when the group has many fields.

Use a tupleUse an object
Pair from a hash function, like [string, number]User profile with named fields
X, Y coordinate, like [number, number]API response with many properties
React useState return: [State, Dispatch]Configuration with optional fields
CSV row with a known column orderAnything with more than three fields

Tuples trade named clarity for brevity. If you find yourself wondering what index 2 was supposed to mean, switch to an object or at least add labels. For data with more than three positional fields, objects are almost always the better choice.

Drawbacks of tuples

Labels on tuple elements are invisible at runtime. If another developer reads the code without an editor that shows tooltips, the meaning of each index position is lost. Consider this when choosing between tuples and objects for shared code.

The push method still works on plain tuples because a tuple is an array at runtime. After pushing an extra element, the type system does not know about it, creating a gap between what TypeScript thinks the array contains and what it actually contains.

Use readonly tuples to block this. For more flexible tuple patterns, see optional tuple elements in TypeScript and variadic tuples in TypeScript.

Rune AI

Rune AI

Key Insights

  • A tuple type is written [TypeA, TypeB, ...] and describes a fixed-length array with per-position types.
  • The compiler knows the exact type at each index and the exact length of the tuple.
  • Accessing an index beyond the tuple length is a compiler error.
  • Destructuring a tuple preserves the per-position types for each variable.
  • Tuples are best for small, convention-based groups; objects are better for larger, named data.
RunePowered by Rune AI

Frequently Asked Questions

How is a tuple different from an array?

An array has one type for all elements and an unknown length. A tuple has a known length and a potentially different type at each index position. Arrays are for homogeneous collections; tuples are for small, fixed-format groups.

Do tuples exist at runtime?

No. A tuple is just an array at runtime. The fixed-length and per-position type information exists only during compilation and is erased from the JavaScript output.

When should I use a tuple instead of an object?

Use a tuple when the position-based meaning is obvious from context, like a [string, number] pair from a hash function, or a React useState return value. Use an object when property names make the data clearer, especially for types with more than three fields.

Conclusion

Tuples are arrays with a known length and per-position types. They shine in small, convention-driven scenarios like key-value pairs and coordinate systems. The compiler checks length and element types at every access, catching out-of-bounds errors before they happen. For complex data with more than a few fields, objects with named properties are usually clearer.