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
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: 1000The shape of it:
const counts: number[] = [0, 0, 0, 0, 0, 0];, one slot per face, all starting at zero.- A
forloop, a thousand times. - One roll:
const roll = Math.floor(Math.random() * 6) + 1;, which chapter 6’sMath.floorand chapter 8’s stretch both prepared you for. counts[roll - 1]++, and that- 1is the whole exercise.- 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.