MediumClass Patterns🐛 Debug Challenge

Private field accessed outside class — wrong data exposed

Buggy Code — Can you spot the issue?

// TypeScript: 'balance' is private — direct access is a compile error
// But at runtime, JS has no enforcement without # syntax

class BankAccount {
  owner;
  balance;  // Should be private — TypeScript: private balance: number

  constructor(owner, initialBalance) {
    this.owner = owner;
    this.balance = initialBalance;
  }

  deposit(amount) {
    if (amount <= 0) throw new Error('Amount must be positive');
    this.balance += amount;
  }

  getBalance() {
    return this.balance;
  }
}

const account = new BankAccount('Alice', 100);

// Bug: directly mutating private field — bypasses validation
account.balance -= 50;  // TypeScript error: 'balance' is private
                        // Runtime: allowed in JS without # syntax

console.log(account.getBalance());
console.log(account.owner);

Fixed Code

class BankAccount {
  owner;
  #balance;  // True JS private field — runtime enforced with # syntax

  constructor(owner, initialBalance) {
    this.owner = owner;
    this.#balance = initialBalance;
  }

  deposit(amount) {
    if (amount <= 0) return; // guard silently for this demo
    this.#balance += amount;
  }

  withdraw(amount) {
    if (amount <= 0 || amount > this.#balance) return; // guard
    this.#balance -= amount;
  }

  getBalance() {
    return this.#balance;
  }
}

const account = new BankAccount('Alice', 100);

// Fix: use the public method — validation runs, privacy preserved
account.withdraw(50);

console.log(account.getBalance());
console.log(account.owner);

Bug Explained

Bug: TypeScript marks balance as private — direct access from outside should be a compile error. Bypassing the deposit/withdraw methods skips validation (e.g., overdraft protection). The fix is to use a withdrawal method.

Explanation: Using # for true JS private fields prevents external access at runtime. The withdraw method contains validation logic — ensuring business rules are always enforced.

Key Insight: TypeScript's private keyword is compile-time only — it's erased at runtime. For true encapsulation, use JavaScript's native #privateField syntax which is enforced at runtime. Always access/mutate private state through public methods that encode business rules.

More Class Patterns Debug Challenges

MediumAbstract class instantiated directly — unexpected behaviorHardClass missing interface implementation — methods silently absent

Practice spotting bugs live →

38 debug challenges with AI hints

🐛 Try Debug Lab