Contents

Chapter 10

Arrays: A List That Grows

push, indexing, and the angle brackets you're told to trust for now.

Chapter 5 split some input and told you to think of the result as a numbered row of strings, with a promise that chapter 10 would explain it properly. Chapter 8 looped over one. Here it is.

Making one

const scores: number[] = [10, 20, 30];

console.log(scores);
console.log(scores.length);
console.log(scores[0]);
[ 10, 20, 30 ]
3
10

number[] is the type: a list of numbers. Square brackets hold the items, .length counts them, and indexing works exactly like it did on strings in chapter 4, starting at zero.

Everything you learned about string positions transfers. The first item is at 0, the last is at .length - 1, and for...of walks them all without you handling any of that:

for (const score of scores) {
  console.log(score);
}
10
20
30

That’s chapter 8’s forward reference paid: for...of works on arrays, and this is the shape you’ll use it on most.

TypeScript can work the type out from the contents, so the annotation is optional here too:

const words = ["a", "b"];

That’s a string[], decided from what’s in it, the same inference that gave let score = 7 its type back in chapter 2.

Growing and shrinking

An array isn’t a fixed size. .push() adds to the end:

const scores: number[] = [10, 20, 30];

scores.push(40);
console.log(scores);
console.log(scores.length);
[ 10, 20, 30, 40 ]
4

.pop() takes the last one back off and hands it to you:

const popped = scores.pop();
console.log(popped);
console.log(scores);
40
[ 10, 20, 30 ]

The type is enforced on the way in:

const scores: number[] = [1, 2];
scores.push("three");
error TS2345: Argument of type 'string' is not assignable to
parameter of type 'number'.

That’s what declaring number[] bought you. Every later line that touches this array can rely on finding numbers in it.

The hole

Now the part this chapter exists for.

const scores = [10, 20, 30];

const missing: number = scores[10];
console.log(missing);

That array has three items. Position 10 is nowhere near it. TypeScript compiles this without a word, and is perfectly happy for you to call the result a number:

undefined

It isn’t a number. It’s undefined, chapter 7’s falsy value, wearing a label that says number.

The label holds right up until you use it:

console.log(missing.toFixed(2));
TypeError: Cannot read properties of undefined (reading 'toFixed')

Exercise 1 · Watch it lie

Make a three-item array, read position 10 into a variable annotated : number, print it, then call .toFixed(2) on it.

Compile it and note that nothing complains. Run it and watch it print undefined and then crash. Two different things went wrong: the type was false, and the crash happened somewhere other than where the mistake was.

That second part is the one worth remembering. The bad index was on one line, the explosion was on another.

The other spelling

There’s a second way to write the type, and you’ll meet it in real code:

const scores: Array<number> = [1, 2];

Array<number> and number[] mean exactly the same thing. The angle brackets are the interesting part: they’re how you tell a general-purpose container what it’s holding. Array on its own is “a list of something”, and <number> fills that in.

You’re going to see those brackets all over TypeScript, and the machinery behind them is called generics. It’s chapter 27, and it’s a real explanation rather than a hand-wave, worth having the reading behind you before you meet it.

Until then number[] is the spelling this book uses, and knowing the two are the same thing is enough.

Check yourself

1. const scores = [10, 20, 30]; const x: number = scores[10]; Does this compile?

Not quite. It cannot. The length is a runtime fact and the index might be a value nobody knows until the program runs.

Yes. This is the first time in the book that a type is simply false. Using x then crashes somewhere other than where the mistake was.

Not quite. There is no default. Reading past the end gives undefined, which is a very different thing from zero.

2. const scores = [1, 2]; scores.push(3); Why does const allow this?

Not quite. Nothing about push is exempt. The rule is about what const actually protects.

Yes. scores = [1, 2] would be blocked. Changing what is inside the array is a different operation, and chapter 13 is about that distinction.

Not quite. Nothing is copied here. The same array gained an item, which is exactly why const did not object.

3. What is the difference between number[] and Array<number>?

Not quite. Both are the same growable array. The size is not what the two spellings differ on.

Yes. The angle-bracket form is how a general-purpose container is told what it holds, which is generics, and chapter 27 explains it properly.

Not quite. Both are checked identically. Pushing a string into either one is the same error.

Project

A thousand dice

Roughly 40 minutes

Roll a six-sided die a thousand times and report how often each face came up. This is the first program in the book whose answer you can’t work out by hand, which changes what “is it right” means.

1: 164
2: 155
3: 153
4: 170
5: 184
6: 174
Total: 1000

The shape of it:

  1. const counts: number[] = [0, 0, 0, 0, 0, 0];, one slot per face, all starting at zero.
  2. A for loop, a thousand times.
  3. One roll: const roll = Math.floor(Math.random() * 6) + 1;, which chapter 6’s Math.floor and chapter 8’s stretch both prepared you for.
  4. counts[roll - 1]++, and that - 1 is the whole exercise.
  5. A second loop to print the tally.

Why roll - 1. Faces run 1 to 6 and positions run 0 to 5. Get this wrong in one direction and face 6 lands at position 6, which doesn’t exist, so counts[6] is undefined and undefined++ gives you NaN. Get it wrong in the other and face 1 lands at position 0 while nothing ever touches the last slot.

Both failures are exactly the hole this chapter was about, and neither one is a compile error.

Always print the total. Add the six counts up and print the sum. It must be exactly 1000. That single line is the only proof you have that every roll landed somewhere, and it catches the off-by-one instantly: a total of 1000 means the tally is sound, and anything else, or a NaN, means a roll went to a slot you didn’t mean.

Getting into the habit of building a check into a program you can’t verify by eye is worth more than this project is.

Stretch: find the most common face without looking at the printed list, using chapter 8’s longest-so-far pattern with a running best. Then raise the rolls to a million and watch the six counts get closer to each other, which is what “fair” actually looks like.