Declare a Non-empty Array in TypeScript

avatar

Borislav Hadzhiev

Tue Mar 01 20222 min read

banner

Photo by Lê Tân

Declare a Non-empty Array in TypeScript #

Use rest elements in tuple type to declare a non-empty array in TypeScript, e.g. let arr: [number, ...number[]] = [1]. The syntax means that the array contains a first element that is a number, but the array is open-ended and may contain more elements.

index.ts
// ✅ Use an inline type let arr: [number, ...number[]] = [1]; // ⛔️ Error: Source has 0 element(s) but target requires 1.ts(2322) arr = []; // ✅ Or use a type alias with a generic type NonEmptyArr<T> = [T, ...T[]]; let arr2: NonEmptyArr<string> = ['a']; arr2 = ['a', 'b']; // ✅ Works // ⛔️ Error: Source has 0 element(s) but // target requires 1.ts(2322) arr2 = [];

We declared a non-empty array by using a tuple, that has a minimum length of 1.

The first example shows how to declare a non-empty array, where the first element is a number and may contain zero or more additional elements.

The ...number[] syntax is called Rest elements in tuple types and indicates that the tuple is open-ended and may have zero or more additional elements of the specified type.

For example, [string, ...boolean[]] means a tuple with a string first element followed by any number of boolean elements.

The tuple can have more than 1 element, but if you try to re-declare it as an empty array, you'd get an error.

index.ts
let arr: [number, ...number[]] = [1]; // ⛔️ Error: Source has 0 element(s) but target requires 1.ts(2322) arr = [];

The tuple type is used to express an array with fixed number of elements whose types are known, but can be different.

You can also use a type alias to type a non-empty array.

index.ts
type NonEmptyArr<T> = [T, ...T[]]; let arr2: NonEmptyArr<string> = ['a']; arr2 = ['a', 'b']; // ✅ Works // ⛔️ Error: Source has 0 element(s) but // target requires 1.ts(2322) arr2 = [];

The NonEmptyArr type is an array that contains at least 1 element of type T, but may have zero or more additional elements of type T.

Using this type alias in conditionals might be confusing:

index.ts
type NonEmptyArr<T> = [T, ...T[]]; const arr3: string[] = ['a']; function getArr(arr: NonEmptyArr<string>) { return arr; } if (arr3.length > 0) { // ⛔️ Error: Argument of type 'string[]' is not // assignable to parameter of type 'NonEmptyArr<string>'. getArr(arr3); }

The easiest way to get around this is to use a type assertion.

index.ts
type NonEmptyArr<T> = [T, ...T[]]; const arr3: string[] = ['a']; function getArr(arr: NonEmptyArr<string>) { return arr; } if (arr3.length > 0) { // ✅ Works getArr(arr3 as NonEmptyArr<string>); }
The tuple type is used to express an array with fixed number of elements whose types are known, but can be different.

Having said that, tuples are represented by an array, and you are still able to call the pop() method to remove elements from the tuple.

index.ts
const arr: [number, ...number[]] = [1]; arr.pop(); // ✅ OK console.log(arr); // 👉️ []

If you want to prevent this from happening, you can declare the tuple as readonly.

index.ts
const arr: readonly [number, ...number[]] = [1]; // ⛔️ Error: Property 'pop' does not // exist on type 'readonly [number, ...number[]]'.ts(2339) arr.pop();

But this also means that you aren't able to change the values of the tuple's elements or add more elements.

Use the search field on my Home Page to filter through my more than 1,000 articles.