Start learning free

LESSON 559 OF 573 · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

Ticket Numbers in TypeScript: Static Members and Instance Fields

Practice O(1) Read the full explanation

A ticket machine gives every new ticket the next number: the first ticket gets 1, the next one 2, and so on. The counter belongs to the class, not to any one ticket: it is a static member of Ticket, one copy shared by all the tickets. Each ticket keeps its own number, copied from the counter when the ticket is created.

Implement the class Ticket:

  • Ticket(name) creates a ticket for name. It adds 1 to the class-level counter and gives the new ticket that value as its number. The ticket keeps its name and its number in two fields, name and number.
  • The static method issued() returns how many tickets were created since the counter was last reset.
  • The static method resetCounter() sets the counter back to 0, so the next ticket gets number 1 again.

For each test case, the test code calls resetCounter() through the class name, then creates one ticket per name, in order. Once all the tickets of the case exist, it prints each ticket as its name, a space, # and its number, one ticket per line, and then the value of issued() on a line of its own.

The starter code spells these names the way your language does: reset_counter in Python, Issued and ResetCounter in C#. In Python the counter is a class attribute and the two methods are marked @staticmethod; in Kotlin and Scala the counter and the two methods live in the companion object, as in the Static Members lesson.

 

Example 1:

Input:
    Ticket("Ann")
    Ticket("Bob")
    Ticket("Cy")
Output:
Ann #1
Bob #2
Cy #3
3
Explanation: resetCounter() set the counter to 0. Each new ticket adds 1 to it and keeps the new value,
so the tickets get 1, 2 and 3, and issued() returns 3.

Example 2:

Input:
    Ticket("Mia")
    Ticket("Mia")
Output:
Mia #1
Mia #2
2
Explanation: The number comes from the shared counter, not from the name, so the two tickets for Mia
get different numbers. The numbers start at 1 again because resetCounter() ran before this case.

Example 3:

Input:
    (no tickets)
Output:
0
Explanation: resetCounter() set the counter to 0 and no ticket was created, so issued() returns 0.

 

Constraints:

  • A test case creates from 0 to 1000 tickets.
  • A name is one word of 1 to 20 English letters. Two tickets can have the same name.
  • The tickets are printed only after all of them were created, so every ticket must store its own number: reading the shared counter when printing would give every ticket the last number.
STEP 1 OF 7
    Ctrl + Enter to check

    TYPESCRIPT · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

    Ticket Numbers in TypeScript, explained

    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

    1. The static counter. static count: number = 0; at the top of the class.
    2. Per-ticket fields. name: string; and number: number; with no static. The property number has the type number: the name comes before the colon, the type after it.
    3. Store the name in the constructor.
    4. Increase the counter with Ticket.count++. A static member is reached through the class name, even inside the class.
    5. Copy the counter into this.number. After a reset, the first ticket finds the counter at 1, so numbering starts at 1.
    6. issued returns Ticket.count.
    7. 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 this object. 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.count works, but a ticket object has no count property.
    • 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 number a getter of the counter. Every ticket would then show the last number.
    • Writing count instead of Ticket.count. A bare count is not a variable in scope inside the class.

    Key points

    • static data 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.
    Keep going with TypeScript500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free