Start learning free

LESSON 561 OF 573 · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

Inventory Item in TypeScript: Private Stock and Checked Changes

Practice O(1) Read the full explanation

A shop keeps track of how many pieces of each item it has: the item's stock. The stock can never go below 0, and it changes only when pieces are sold or new ones arrive. So the stock is a private field, changed only through two methods that check the rules. The item's name has no rules to protect, so it stays a field any code can read.

Implement the class InventoryItem:

  • InventoryItem(name, stock) creates an item with that name and that many pieces in stock. The name is kept in a field name that other code can read.
  • sell(n) sells n pieces. If n is at least 1 and at most the stock, it lowers the stock by n and returns true. Otherwise it changes nothing and returns false: selling 0 pieces, a negative number of pieces, or more pieces than there are in stock is refused.
  • restock(n) adds n pieces to the stock when n is at least 1, and ignores n otherwise. It returns nothing.
  • getStock() returns the stock. It is the only way for other code to read it.

Keep the stock private the way the Access Modifiers lesson does in your language: self._stock in Python (private by convention), a #stock field in JavaScript, and the private keyword in Java, C++, C#, TypeScript, Kotlin, Scala and Swift. In Kotlin and Scala the constructor's second parameter is named initialStock, and the private stock starts at its value.

Each test case creates one item, then applies a list of operations to it, in order. The output has one line per test case: the item's name and a colon, then what each sell and getStock call returns, separated by spaces, with sell's answer printed as True or False. The starter code spells these names the way your language does: get_stock in Python, Sell, Restock and GetStock in C#.

 

Example 1:

Input:
    InventoryItem("Pencil", 5)
    sell(3)
    getStock()
    sell(5)
    getStock()
    restock(10)
    getStock()
Output: Pencil: True 2 False 2 12
Explanation: Selling 3 of the 5 pencils works and leaves 2. Selling 5 more is refused, because only 2 are left,
and the stock stays 2. restock(10) brings it to 12.

Example 2:

Input:
    InventoryItem("Mug", 4)
    sell(4)
    getStock()
    sell(1)
    getStock()
Output: Mug: True 0 False 0
Explanation: Selling all 4 mugs is allowed and leaves 0. With nothing left, even one mug can't be sold.

Example 3:

Input:
    InventoryItem("Lamp", 2)
    sell(0)
    sell(-3)
    restock(0)
    restock(-5)
    getStock()
    restock(1)
    sell(3)
    getStock()
Output: Lamp: False False 2 True 0
Explanation: Selling 0 or -3 lamps is refused, and restocking 0 or -5 lamps is ignored, so the stock is still 2.
After restock(1) there are 3 lamps, and selling all 3 works.

 

Constraints:

  • A name is one word of English letters. 0 <= stock <= 104 when the item is created.
  • -104 <= n <= 104 in every sell and restock.
  • A test case has from 1 to 1000 operations, and at least one of them is a sell or a getStock.
STEP 1 OF 7
    Ctrl + Enter to check

    TYPESCRIPT · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

    Inventory Item in TypeScript, explained

    Problem overview

    A shop tracks how many pieces of an item it has. The stock can never go below 0, and it changes only when pieces are sold or arrive. So the stock is a private field changed only by two methods that check the rules. The name has no rules to protect, so it is a public field.

    • new InventoryItem(name, stock) creates the item.
    • sell(n) sells n pieces. If n is at least 1 and at most the stock, it lowers the stock and returns true. Otherwise it changes nothing and returns false.
    • restock(n) adds n when n is at least 1 and ignores it otherwise. It returns nothing.
    • getStock() returns the stock.

    For a pencil with stock 5, sell(3) returns true and leaves 2. Then sell(5) is refused, because only 2 are left. restock(10) makes it 12.

    Approach

    Check first, change second. In sell, refuse the bad cases with an early return false; whatever reaches the rest of the method is a valid sale. Selling exactly the stock is allowed, so the upper test is n > this.stock, not >=. Every method runs in constant time.

    Step by step

    1. Public name. name: string; with no modifier is public.
    2. Private stock. private stock: number; Only code inside the class can use it.
    3. Constructor. Store both parameters in the fields. Inside the class, private doesn't get in the way.
    4. sell, refuse. If n < 1 || n > this.stock, return false before changing anything.
    5. sell, go through. Subtract n and return true.
    6. restock. Add n only when n >= 1.
    7. getStock returns the stock.

    The solution in TypeScript

    class InventoryItem {
        name: string;
        private stock: number;
    
        constructor(name: string, stock: number) {
            this.name = name;
            this.stock = stock;
        }
    
        sell(n: number): boolean {
            if (n < 1 || n > this.stock) {
                return false;
            }
            this.stock -= n;
            return true;
        }
    
        restock(n: number): void {
            if (n >= 1) {
                this.stock += n;
            }
        }
    
        getStock(): number {
            return this.stock;
        }
    }
    
    • || means "or": the sale is refused if either condition holds.
    • Parameter and field with the same name. In the constructor, this.stock is the field and stock is the parameter.
    • Return types. sell returns boolean, restock returns void.

    Using the class

    const p = new InventoryItem("Pencil", 5);
    console.log(p.name);
    console.log(p.sell(3), p.getStock(), p.sell(5), p.getStock());
    p.restock(10);
    console.log(p.getStock());
    const l = new InventoryItem("Lamp", 2);
    console.log(l.sell(0), l.sell(-3));
    l.restock(0);
    l.restock(-5);
    console.log(l.getStock());
    l.restock(1);
    console.log(l.sell(3), l.getStock());
    
    Pencil
    true 2 false 2
    12
    false false
    2
    true 0
    

    The lesson's tests print the booleans as True and False. Selling 0 or -3 is refused, restocking 0 or -5 is ignored, and selling all 3 lamps is allowed and leaves 0.

    Outside the class, p.stock is a compile error: the property is private and only accessible within the class InventoryItem. Because the compiler still writes the JavaScript, an assignment like m.stock = 100 would run if you left the error in place, and getStock() would then return 100. Fix the error, not the output.

    Why the checks live in the methods

    If the stock were a public field, any code could write item.stock = -4, and the shop would "have" a negative number of pieces. With a private field, the only ways to change the stock are sell and restock, and each of them refuses a change that would break a rule. A refused sale also tells the caller so with false, which is why sell returns a boolean while restock, which can only ignore bad input, returns nothing.

    The name is different. Nothing about a name can be wrong in a way the class cares about, so making it public avoids writing a getter that does nothing.

    Testing

    • A pen with stock 5: sell(6) returns false and the stock stays 5; sell(5) returns true and the stock is 0; after restock(3) the stock is 3.
    • A mug with stock 4: sell(4) returns true and leaves 0, then sell(1) returns false and the stock stays 0.

    Another way: one condition

    The lesson refuses first and goes ahead after. You can also test the allowed range once, with &&, and change the stock inside the if. Parameter properties shorten the constructor too:

    class InventoryItem {
        constructor(public name: string, private stock: number) {}
        sell(n: number): boolean {
            if (n >= 1 && n <= this.stock) {
                this.stock -= n;
                return true;
            }
            return false;
        }
        getStock(): number { return this.stock; }
    }
    const i = new InventoryItem("Pencil", 5);
    console.log(i.name, i.sell(3), i.getStock(), i.sell(5), i.getStock());
    

    This prints Pencil true 2 false 2. The two versions are equivalent. The lesson's early return keeps the happy path unindented, and it reads as "refuse what can't be sold, then sell".

    Edge cases and common mistakes

    • Selling everything. n equal to the stock is allowed and leaves 0. With nothing left, even sell(1) is refused.
    • Negative or zero amounts. sell(0) and sell(-3) return false; restock(0) and restock(-5) do nothing. A negative restock must not reduce the stock.
    • Changing before checking. Lowering the stock first and then testing could leave it below 0.
    • Making the name private. The name is meant to be readable, so it stays public.

    Key points

    • Make the stock private and expose methods that check the rules, so the stock can never go below 0.
    • Leave a field public when it has no rules to protect, like the name.
    • Check before you change: a refused sale must leave the stock untouched.
    • Return a boolean when the caller needs to know whether an operation worked, and nothing when it can only be ignored.
    • Each method does one comparison or one addition, so it runs in constant time.
    Keep going with TypeScript500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free