Skip to content
Theory · Arrays and tuples · lesson 1 of 7

Array types

What you'll learn

What you'll learn:

  • write an array type as number[] and read the type TypeScript inferred on its own;
  • forbid changes to an array with the type readonly number[];
  • describe a tuple, an array where each position has its own type, such as [string, number];
  • keep the exact values of an array or an object in its type with as const.
A row of identical lockers number[] and an ID card with two slots [0] and [1]

Picture a row of lockers in a left-luggage office. A sign above the row says "Suitcases only". The lockers are numbered from zero, like the elements of an array, and each one holds the same kind of thing: a suitcase. A clerk who opens any of them knows in advance what will come out.

In JavaScript an array has no such sign. Anything can go into it, and the mistake shows up later, for example when the values are added up. Here is a program that totals scores:

TypeScript
const scores = [90, 75];
scores.push("100"); // ✗
let total = 0;
for (const s of scores) {
    total += s;
}
console.log(total);
TEXT
error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.

tsc reports that an argument of type string cannot be passed to a parameter of type number. TypeScript looked at the literal [90, 75] and hung a "numbers only" sign over the array, so the string "100" did not get through push.

If this JavaScript ran, the program would print 165100. The string "100" was added to the number 165, and the + operator glued them into one string. TypeScript finds this mistake on the line with push, not where the odd result shows up.

The "array of numbers" type

An array type is written as the element type followed by a pair of square brackets: number[] reads "array of numbers", string[] reads "array of strings". This is the sign over the row of lockers: it says what each element holds.

For const scores = [90, 75], TypeScript inferred the type number[] on its own, and the editor's hover shows const scores: number[]. You write the type by hand where there is nothing to infer it from, for example on a function parameter:

TypeScript
function average(nums: number[]): number {
    let sum = 0;
    for (const n of nums) {
        sum += n;
    }
    return sum / nums.length;
}

console.log(average([90, 75, 60])); // 75

Let's go through the example line by line:

  1. nums: number[] means the function takes an array of numbers, and a call with an array of strings does not compile.
  2. In the for…of loop, n has the type number: TypeScript reads it off the array's sign.
  3. sum / nums.length is a number, so the result matches the number annotation.
  4. The program prints 75.

An array type checks every place where the array takes an element in or hands one out:

  • scores.push(x) accepts only an x of type number;
  • a write by index, scores[0] = "100", fails too: error TS2322, a string cannot be assigned to the type number;
  • a read, scores[0], gives a number, so you can call toFixed and the other number methods on the element.

Two ways to write one type

The type of an array of numbers can be written in two ways.

The number[] form

The short form: the element type first, then []. It is the one you see more often in code, and the course uses it.

The Array<number> form

The long form: the word Array with the element type in angle brackets. It is exactly the same type. If you write const nums: Array<number> = [1, 2];, the editor's hover still shows number[].

Interviewers sometimes ask how number[] differs from Array<number>. It doesn't: these are two ways to write one type, and the choice between them is a matter of the team's style.

An array of mixed values

If a literal holds values of different types, TypeScript infers an array of a union. For const mixed = ["Аня", 30]; the hover shows (string | number)[]: each element is a string or a number.

The parentheses change the meaning here:

  • (string | number)[] is an array in which any element is a string or a number;
  • string | number[] is a single string or an array of numbers.

The sign over such a row says "suitcases or bags". Taking an item out of a locker, the clerk does not know what is in their hands. They may only do with it what suits both a suitcase and a bag. The same goes for the element mixed[0]: TypeScript allows only what string and number have in common.

Here the left-luggage analogy ends. The number of lockers in a row is fixed in advance, while an array grows. Each push adds a new locker with the same sign to the end of the row.

An empty array

There is nothing to infer the element type from in the literal [] itself: it holds no elements at all. So you write the type of an empty array yourself: const names: string[] = [];.

Without an annotation, TypeScript behaves differently depending on where the empty array is:

  • in a variable, it follows the lines below: after const names = []; and names.push("Аня");, names has the type string[];
  • but until something is put into it, you can neither pass the array to a function nor take an element out: tsc reports an implicit any[] type;
  • in an object property, TypeScript does not follow the lines below and infers never[] at once.

Common mistake

An empty array was put into an object property without a type:

TypeScript
const cart = { items: [] };
cart.items.push("apple"); // ✗
TEXT
error TS2345: Argument of type '"apple"' is not assignable to parameter of type 'never'.

For the property items, TypeScript inferred the type never[], an array that nothing can be put into: the type never accepts no values at all. That is why the message says an argument of type "apple" cannot be passed to a parameter of type never.

The fix is to name the element type in advance:

TypeScript
type Cart = { items: string[] };

const cart: Cart = { items: [] };
cart.items.push("apple");
console.log(cart.items.length); // 1

Key points

  • An array type is the element type plus brackets: number[]; Array<number> is another way to write the same type.
  • TypeScript infers the type from the literal: [90, 75] is a number[], and push("100") into it is error TS2345.
  • A literal of values of different types gives an array of a union: (string | number)[].
  • You write the type of an empty array yourself, or in an object property it becomes never[].