You already know how to store a list of the same kind of thing — thirty test scores in one
You could keep three separate variables — pupilName, pupilAge,
pupilPresent — but they'd float about with nothing tying them together, and for a
whole class you'd be back to the mess arrays were meant to solve. What you want is a way to bundle
those related facts into one thing. That thing is a record.
A record groups several named fields — of possibly different types — into a single value. Here is one pupil as a record:
The curly braces { } make a record; inside, each line is a field: a
name, a colon, and a value. One tidy parcel holds everything about Aisha.
This is the big idea, and it's what makes a record different from an array. In an array you reach
an element by its number (its index): scores[2]. In a record you
reach a field by its name, using a dot: pupil.name. You don't have
to remember that the age was "the second one" — you just ask for .age.
Reading a field is called accessing it, and the dot is how you do it. Run this and see each field come out by name:
Notice how much clearer pupil.age reads than pupil[1] would. The field
names document themselves — anyone reading the code can see exactly what each value means.
interface
If every pupil has the same three fields, it would be nice to give that shape a name and
write it down once. In TypeScript you do that with an interface. An interface
doesn't hold any data itself — it's a description that says "a Pupil
is anything with a name that's text, an age that's a number, and an
isPresent that's true/false":
Writing const aisha: Pupil tells TypeScript "this record must match the
Pupil shape". Now the computer will check your work: if you forget a
field, misspell one, or put text where a number belongs, it warns you before the program
even runs. That safety net — describing the shape of your data and having it checked — is your
first taste of typed object-oriented programming, and you'll meet it again and
again.
Records aren't frozen. You can update a single field with the dot on the left of
an =, leaving the others untouched. When Aisha arrives late, we flip just her
isPresent field and give her a birthday too:
Read aisha.age = aisha.age + 1 as "the new age is the old age plus one" — exactly
like updating an ordinary variable, but the value lives inside the record, reached by its
field name.
Here's where records and arrays team up beautifully. One record describes one pupil; an
array of records describes the whole class. Each element is a full
record, so we get numbered access to the pupils and named access to each pupil's fields —
register[i].name means "the name field of the pupil at index i".
This is how nearly all real data is stored: a list of records. A music app is an array of
song records (title, artist, lengthInSeconds); a game is an
array of enemy records (x, y, health). Loop over the array,
pull the fields you need by name, and you can answer any question about the whole collection.
No — and that's another way records differ from arrays. Because you fetch a field by its
name, the order you write the fields in doesn't matter:
{ name: "Ben", age: 14 } and { age: 14, name: "Ben" } are the
same record. In an array the order is everything (index 0 is the first element,
full stop); in a record the labels do the work, so you're free to write them however
reads best.
The classic mix-up is treating a record like an array, or the other way round. Keep the two straight:
scores[2];pupil.age.
So you access a record's field with a dot and the field name
(pupil.name) — not with a number in square brackets. Writing
pupil[0] hoping to get the name doesn't work: there is no field called "0". And
watch the spelling — a record field's name must match exactly. Ask for a field that
isn't there and TypeScript hands back undefined, the same quiet
trap as running off the end of an array: