Problem overview
A ticket machine gives each new ticket the next number: 1, 2, 3, and so on. The counter belongs to the class Ticket, not to any one ticket, so there is a single copy shared by all tickets. Each ticket keeps its own name and its own number, copied from the counter when the ticket is created.
The class also has two static methods: Ticket.issued() returns how many tickets were created since the last reset, and Ticket.resetCounter() sets the counter back to 0.
Approach
The key is to separate shared data from per-object data. The counter is static: you reach it as Ticket.count, with no object needed. The name and the number are ordinary fields, one copy per ticket.
The number must be stored in the ticket. If a ticket read Ticket.count when asked for its number, it would report the latest number, because the counter keeps moving. Copying the value into this.number at creation freezes it.
Step by step
- The static counter.
static count: number = 0;at the top of the class. - Per-ticket fields.
name: string;andnumber: number;with nostatic. The propertynumberhas the typenumber: the name comes before the colon, the type after it. - Store the name in the constructor.
- Increase the counter with
Ticket.count++. A static member is reached through the class name, even inside the class. - Copy the counter into
this.number. After a reset, the first ticket finds the counter at 1, so numbering starts at 1. - issued returns
Ticket.count. - resetCounter assigns 0 to
Ticket.count.
The solution in TypeScript
class Ticket {
static count: number = 0;
name: string;
number: number;
constructor(name: string) {
this.name = name;
Ticket.count++;
this.number = Ticket.count;
}
static issued(): number {
return Ticket.count;
}
static resetCounter(): void {
Ticket.count = 0;
}
}
- Static methods have no
thisobject. They are called on the class, with no ticket at all, so they can only use static members. - Static members aren't on the objects.
Ticket.countworks, but a ticket object has nocountproperty. - Increment first, then copy. The first ticket gets 1 because the counter is increased before it is copied.
Using the class
Ticket.resetCounter();
const t1 = new Ticket("Ann");
const t2 = new Ticket("Bob");
const t3 = new Ticket("Cy");
for (const t of [t1, t2, t3]) {
console.log(t.name + " #" + t.number);
}
console.log(Ticket.issued());
Ticket.resetCounter();
console.log(Ticket.issued());
const m1 = new Ticket("Mia");
const m2 = new Ticket("Mia");
console.log(m1.number, m2.number, Ticket.issued());
console.log(t1.number, Ticket.count);
Ann #1
Bob #2
Cy #3
3
0
1 2 2
1 2
The last line shows the split: t1 still has number 1 while the shared counter has moved on to 2. Two tickets for "Mia" get different numbers, because the number comes from the counter, not from the name.
Shared data and per-object data
It helps to ask of every piece of data: does this belong to the whole class, or to one object? The counter answers "how many tickets exist", a question about the machine, so it is static. The name and number answer questions about one ticket, so they are instance fields.
The same idea shows in how you reach them. After two tickets, Ticket.count is 2, and a.count on a ticket object is undefined, as is a.issued: static members live on the class, not on its objects. TypeScript's compiler also reports a.count as an error, pointing you to Ticket.count.
A trace
After Ticket.resetCounter(), creating ticket a makes the counter 1 and gives a.number the value 1. Creating ticket b makes the counter 2 and gives b.number the value 2, while a.number is still 1. If number instead returned the counter whenever it was read, both tickets would report 2, which is exactly the mistake the lesson's note warns about.
A reset between batches matters in the same way. After a second resetCounter() and one new ticket, that ticket's number is 1 again.
What a static field is at run time
When TypeScript compiles for ES2022, a static field stays a static field of the JavaScript class. It is a property of the Ticket function itself, next to the constructor, and no ticket object gets a copy. That is exactly why every ticket sees the same counter, and why Ticket.count keeps its value as tickets come and go.
Nothing prevents other code from assigning Ticket.count = 100, because the counter is public in this lesson. That is fine for a small exercise: the lesson gives you the two static methods as the intended way to read and reset the counter.
Edge cases and common mistakes
- No tickets. After a reset and no
new Ticket,issued()returns 0. - Forgetting the reset. Without
resetCounter(), a second batch of tickets continues from where the first stopped. - Making
numbera getter of the counter. Every ticket would then show the last number. - Writing
countinstead ofTicket.count. A barecountis not a variable in scope inside the class.
Key points
staticdata belongs to the class and exists once; ordinary fields belong to each object.- Reach static members through the class name, inside the class as well as outside it.
- Copy the counter into the object when it is created, or every ticket would show the latest number.
- Reset the counter before each independent batch, or numbering continues from the last one.
