Difference between tuples and arrays in TypeScript

In TypeScript, tuples and arrays have similar syntax, but they are different in their usage and behavior. the choice between using tuples and arrays primarily comes down to the specific requirements and constraints of your data. Understanding when to use tuples as opposed to arrays is crucial for writing type-safe and efficient code.

let person: [string, number] = ["Alice", 30];
let fruits: string[] = ["apple", "banana", "cherry"];

TLDR; The difference between tuples and arrays in TypeScript is that tuples are more strict and precise than arrays. Tuples can store only a fixed number of elements and types at each index, while arrays can store any combination of types and the order is not important.

Common use cases

Tuples are useful when you want to store data that has a fixed and known structure, such as coordinates, key-value pairs, function parameters and return types, etc.

// Declare a tuple type that represents a point on a 2D plane
type Point = [number, number];
let point: Point = [3, 4];

// Declare a tuple type that represents a key-value pair
type KeyValuePair = [string, any];

let nameAge: KeyValuePair = ["Alice", 25];
let colorCode: KeyValuePair = ["red", "#FF0000"];

// Function return type
function getPersonInfo(): [string, number] {
  return ["Alice", 30];
}

Arrays are useful when you want to store data that has a variable and unknown structure, such as lists of items, collections of values, multidimensional arrays, etc.

let names: string[] = ["Alice", "Bob", "Charlie"];

let mixedData: (string | number | boolean)[] = ["apple", 42, true, "banana", 7, false];

let matrix: number[][] = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

Type inteference

TypeScript infers the type of an array literal based on the elements you provide when initializing the array. If all the elements in the array literal are of the same type, TypeScript infers that the array is of that specific type. If the elements in the array literal have different types, TypeScript infers the type as a union of those types. If you initialize an empty array, TypeScript generally infers it as an array of the any type.

let numbers = [1, 2, 3]; // TypeScript infers: numbers: number[]
let mixedData = [1, "two", true]; // TypeScript infers: mixedData: (number | string | boolean)[]
let emptyArray = []; // TypeScript infers: emptyArray: any[]

TypeScript requires you to use a type annotation to indicate that you want a tuple type instead of an array type. TypeScript uses type annotations to distinguish between arrays and tuples, and to enforce the shape and length of tuples at compile time.

let person: [string, number] = ["Alice", 30];

Length

Tuples in TypeScript are designed to have a fixed length in terms of the types of elements they can contain. This means that when you declare a tuple, you specify the data types for each position, and TypeScript enforces these types.

However, you can still add elements to a tuple using the push method because TypeScript does not prevent you from doing so. This can be a source of confusion.

let myTuple: [string, number] = ["Alice", 30];

// You can push elements to the tuple even though it's not a recommended practice.
myTuple.push("Bob", 25); // This is allowed, but not type-safe.

console.log(myTuple); // Output: ["Alice", 30, "Bob", 25]

As you can see, you can push additional elements to the tuple, but this is not considered a recommended practice because it can lead to unexpected behavior.

Arrays have a variable length, which means you can add or remove elements from them using methods like push, pop, shift, unshift, etc. This also means that you can assign an array value to another array type that has the same element type but a different length.

let numbers: number[] = [1, 2, 3];
console.log(numbers); // [1, 2, 3]

// Add more elements to the array
numbers.push(4, 5);
console.log(numbers); // [1, 2, 3, 4, 5]

// Remove elements from the array
numbers.pop();
console.log(numbers); // [1, 2, 3, 4]

Element data types

Tuples in TypeScript allow you to specify the data type for each element at a specific position. This means that each element in a tuple has a fixed, known data type, and the data type is enforced.

let person: [string, number, boolean] = ["Alice", 30, true];

Arrays can contain elements of the same data type or elements of varying data types. It offers flexibility in terms of the data it can hold.

let numbers: number[] = [1, 2, 3, 4, 5];
let mixedData: (string | number | boolean)[] = ["apple", 42, true, "banana", 7, false];

Order of elements

Tuples preserve the order of elements, while arrays do not. This means that tuples are more strict and precise than arrays. The order and position of elements in a tuple are guaranteed, while in an array, the order is not important.

// Create a tuple value that matches this type
let data: [string, number, boolean] = ["hello", 42, true];

// Try to swap the order of the elements
data = [true, "hello", 42]; // Error: Type 'boolean' is not assignable to type 'string'

// Create an array value that matches this type
let values: any[] = ["hello", 42, true];

// Swap the order of the elements
values = [true, "hello", 42]; // OK

Accessing elements

Tuples are more rigid and don’t offer the same array methods and properties. You mainly access tuple elements using indexing and destructuring.

let person: [string, number] = ["Alice", 30];

// Using indexing
console.log(person[0]); // "Alice"
console.log(person[1]); // 30

// Using destructuring
let [name, age] = person;
console.log(name); // "Alice"
console.log(age); // 30

Accessing array elements is a fundamental aspect of working with arrays in programming, and there are several methods to achieve this.

let arr: number[] = [1, 2, 3, 4, 5];

// Using indexing
console.log(arr[0]); // 1
console.log(arr[1]); // 2

// Using destructuring
let [first, second, ...rest] = arr;
console.log(first); // 1
console.log(second); // 2
console.log(rest); // [3, 4, 5]

// Using for...of loop
for (let element of arr) {
  console.log(element);
}
// 1
// 2
// 3
// 4
// 5

// Using array methods
arr.push(6);
console.log(arr); // [1, 2, 3, 4, 5, 6]