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)sellsnpieces. Ifnis at least 1 and at most the stock, it lowers the stock and returnstrue. Otherwise it changes nothing and returnsfalse.restock(n)addsnwhennis 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
- Public name.
name: string;with no modifier is public. - Private stock.
private stock: number;Only code inside the class can use it. - Constructor. Store both parameters in the fields. Inside the class,
privatedoesn't get in the way. - sell, refuse. If
n < 1 || n > this.stock, returnfalsebefore changing anything. - sell, go through. Subtract
nand returntrue. - restock. Add
nonly whenn >= 1. - 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.stockis the field andstockis the parameter. - Return types.
sellreturnsboolean,restockreturnsvoid.
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; afterrestock(3)the stock is 3. - A mug with stock 4:
sell(4)returns true and leaves 0, thensell(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.
nequal to the stock is allowed and leaves 0. With nothing left, evensell(1)is refused. - Negative or zero amounts.
sell(0)andsell(-3)return false;restock(0)andrestock(-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
privateand 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.
