Arrays

A typed array promises that every element is the same kind of value. TypeScript checks what goes in, knows what comes out, and can even lock an array against changes with readonly.

Two ways to write an array type

TS arrays.ts
let scores: number[] = [88, 92, 79];
let names: Array<string> = ["Ana", "Ben"];

scores.push(95);
names.push(3);
tsc output
error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.

number[] and Array<number> mean exactly the same thing. The bracket form is shorter and more common; the Array<…> form is the generic syntax you'll meet properly in lesson 20. Pick one and stay consistent.

Inferred element types

When you initialise an array with values, TypeScript infers the element type, and every method that reads from it knows that type:

TS infer.ts
const prices = [4.5, 12, 7.25];

const total = prices.reduce((sum, p) => sum + p, 0);
const labels = prices.map(p => `$${p.toFixed(2)}`);

console.log(total);
console.log(labels);
Output
23.75
[ '$4.50', '$12.00', '$7.25' ]

prices is number[], so p inside each callback is a number, and labels comes out as string[] — no annotations needed.

An array literal containing mixed values gets a union element type. [1, "two"] is inferred as (string | number)[]: each element may be either, and you'll need to check which before using string- or number-specific methods.

Empty arrays need a type

An empty array gives inference nothing to go on, so annotate it:

TS empty.ts
const queue: string[] = [];

queue.push("build");
queue.push("test");

console.log(queue.length, queue[0]);
Output
2 build

Reading past the end

By default, TypeScript assumes an index you read actually exists:

TS index.ts
const colors = ["red", "green"];
const third = colors[2];

console.log(third);
Output
undefined

third is typed as string, but its value is undefined. The compiler option noUncheckedIndexedAccess (lesson 38) changes indexed reads to string | undefined, forcing you to handle the missing case. Many teams turn it on for exactly this reason.

Readonly arrays

Put readonly before an array type and every mutating method disappears from it:

TS readonly.ts
const weekdays: readonly string[] = ["Mon", "Tue", "Wed", "Thu", "Fri"];

console.log(weekdays.includes("Sat"));
weekdays.push("Sat");
tsc output
error TS2339: Property 'push' does not exist on type 'readonly string[]'.

Non-mutating methods like map, filter, slice and includes still work, because they return new arrays. ReadonlyArray<string> is the long-hand spelling of the same type.

Readonly parameters are a great way to promise callers that a function won't modify their data:

TS readonly-param.ts
function average(values: readonly number[]): number {
  let sum = 0;
  for (const v of values) sum += v;
  return values.length ? sum / values.length : 0;
}

const temps = [18, 21, 24];
console.log(average(temps));
Output
21

A normal number[] can be passed where a readonly number[] is expected, but not the other way around.

Arrays of objects

Element types can be anything, including object shapes (more on those in lesson 08):

TS objects.ts
const cart: { name: string; qty: number }[] = [
  { name: "pen", qty: 3 },
  { name: "pad", qty: 1 },
];

const itemCount = cart.reduce((n, line) => n + line.qty, 0);
console.log(itemCount);
Output
4

Try it yourself

Try it yourself
Console output
Note: readonly is a compile-time check only. At runtime, the array is a normal JavaScript array. If you need an array that's frozen even for untyped code, also call Object.freeze() on it.