Arrays and Tuples

In TypeScript, arrays and tuples offer structured ways to store collections of values. While arrays are typically dynamic in length and homogeneous in type, tuples allow defining specific data types at fixed indices. However, with variadic tuple types, tuples can gain flexibility, enabling more dynamic structures as we will explore in this chapter.

Typing Arrays

To define an array in TypeScript, we specify the type of its elements using square brackets (number[]), or alternatively, the generic Array<T> syntax where T is a placeholder for the specific type:

function add(numbers: number[]) {
  return numbers.reduce((sum, current) => sum + current, 0);
}

The add function above accepts an array of numbers and sums them up using .reduce(). The alternative syntax looks like this:

function add(numbers: Array<number>) {
  return numbers.reduce((sum, current) => sum + current, 0);
}

While both notations are functionally equivalent, number[] is the preferred shorthand when using primitive data types. Especially when dealing with multidimensional arrays, number[][] is usually clearer than Array<Array<number>>.

The Array<number> syntax is consistent with generic types (Promise<T>, Map<K, V>, etc.), making it conceptually appealing to developers with an object-oriented programming background.

Indexing Array Values

TypeScript also allows you to define arrays where the index is a string or number and ensures that the elements follow a specific type. Here's an example with an index signature:

type IndexedArray = { [index: number]: string };
 
const stringArray: IndexedArray = ['Hello', 'World'];
console.log(stringArray[0]); // "Hello"

The index name can be chosen freely, but it should be meaningful in the context of your data:

type UsersArray = { [userId: number]: string };
 
const users: UsersArray = ['Benny', 'Sofia'];
console.log(users[0]); // "Benny"

Using Type Aliases for Arrays

To improve readability, we can define a type alias, which gives a name to an array type:

type Payments = number[];
 
function add(numbers: Payments) {
  return numbers.reduce((sum, current) => sum + current, 0);
}

This approach improves clarity and reusability, particularly when the type represents a domain-specific concept like Payments.

Mixing Types in Arrays

By using a union type, you can enable an array to contain multiple different value types.

type Payments = (number | string)[];
 
function add(numbers: Payments) {
  return numbers
    .map((num) => (typeof num === 'number' ? num : parseInt(num)))
    .reduce((sum, current) => sum + current, 0);
}

Tuples: Defining Fixed-Length Arrays

A tuple in TypeScript is an array with a fixed number of elements, where each position has a specific type. For example, a Payments tuple with exactly four numbers:

type Payments = [number, number, number, number];
 
function add(numbers: Payments) {
  return numbers.reduce((sum, current) => sum + current, 0);
}
 
console.log(add([1, 2, 3, 4]));

Attempting to pass an array with more or fewer elements will result in a TypeScript error.

Mixing Types in Tuples

Tuples allow different types in different positions. For example, if the first and third value in the array must be a string, we can define it as follows:

type Payments = [string, number, string, number];
 
function add(payments: Payments) {
  return payments.filter((value) => typeof value === 'number').reduce((sum, current) => sum + current, 0);
}
 
console.log(add(['January', 50, 'February', 30])); // 80

Labeling Elements in Tuples

To improve readability, TypeScript allows labeling tuple elements:

type Payments = [month: string, payment: number, month: string, payment: number];

Labeled tuples improve clarity, making it evident what each value in the tuple represents.

Variadic Tuples: Allowing Flexible Lengths

With variadic tuple types, we can specify a tuple where the last part of the array can contain an arbitrary number of elements of a specific type. This is done using the rest syntax (...):

type Payments = [number, string, ...number[]];
 
function add(numbers: Payments) {
  return numbers
    .map((num) => (typeof num === 'number' ? num : parseInt(num)))
    .reduce((sum, current) => sum + current, 0);
}
 
console.log(add([1, '2', 3, 4, 5, 6, 7, 8]));

This allows any number of additional number elements beyond the required number, string combination.


What You Have Learned

Typing Arrays in TypeScript: You have learned that arrays in TypeScript can be typed using either the number[] shorthand or the Array<number> syntax. Both methods ensure type safety when working with collections of values.

Tuples for Fixed-Length Data Structures: You have seen how tuples allow defining arrays with fixed lengths and specific types at each index. Unlike regular arrays, tuples enforce structure, ensuring predictable data formats.

Variadic Tuples for Dynamic Length: You now understand how variadic tuples allow a flexible number of elements in a tuple using the rest syntax (...), enabling dynamic yet structured data storage.

Labeling Elements: You have learned that tuples can store different types at different positions and that elements can be labeled to improve clarity. For example, [month: string, payment: number] makes it clear what each element represents.


Quiz

  1. Which of the following is the correct way to type an array of numbers in TypeScript?
  • a) number[]
  • b) Array[number]
  • c) numbers[]
  • d) Array<number[]>
  1. What is a key difference between arrays and tuples in TypeScript?
  • a) Arrays must always contain only one type, while tuples can mix different types.
  • b) Tuples always have a fixed length, while arrays can have a dynamic length.
  • c) Arrays require type annotations, but tuples do not.
  • d) Tuples can only store numbers and strings, while arrays can store any type.
  1. Which of the following correctly defines a tuple with two values: a string and a number?
  • a) [string, number]
  • b) {string, number}
  • c) [string | number]
  • d) (string, number)
  1. How can you define an array that allows both number and string values?
  • a) Array<string | number>
  • b) number | string[]
  • c) [number | string]
  • d) string[] | number[]
  1. Which statement best describes variadic tuples in TypeScript?
  • a) They allow tuples to accept a dynamic number of elements of a specified type.
  • b) They enforce that tuples always have the exact same number of elements.
  • c) They allow any data type to be stored at any index in an array.
  • d) They disable type checking for tuple values.
  1. What is the advantage of labeling elements in a tuple, such as [month: string, payment: number]?
  • a) It prevents TypeScript from checking the tuple’s length.
  • b) It improves code readability by making it clear what each element represents.
  • c) It allows the tuple to store unlimited elements.
  • d) It changes the tuple into a regular array.