Part 2: Encapsulation and Access Modifiers - Data Protection

Introduction

My first production bug came from exposing a balance property directly on an Account class. A developer accidentally set account.balance = -500. The validation logic in the debit() method was bypassed. Negative balances cascaded through the system causing financial discrepancies.

That's when I learned encapsulation. By making balance private and only allowing modifications through controlled methods, I prevented direct manipulation. This article teaches you how to protect data integrity using access modifiers, getters/setters, and information hiding.

The Problem: Direct Property Access

class BankAccount {
    balance: number;  // Public by default - anyone can modify!
    
    constructor(initialBalance: number) {
        this.balance = initialBalance;
    }
    
    withdraw(amount: number): void {
        if (amount > this.balance) {
            throw new Error('Insufficient funds');
        }
        this.balance -= amount;
    }
}

const account = new BankAccount(1000);

// Problem: Can bypass validation!
account.balance = -500;  // Direct modification - no validation!
console.log(account.balance);  // -500

// Or accidentally overwrite
account.balance = undefined;  // Oops!

Issues:

  • No validation when setting balance

  • Business rules bypassed

  • Data integrity broken

  • Hard to track who modified what

The Solution: Encapsulation

Access Modifiers in TypeScript

Public (Default)

Accessible everywhere:

Private

Only accessible within the class:

Protected

Accessible within class and subclasses:

Getters and Setters

TypeScript provides elegant getter/setter syntax:

Basic Getters and Setters

Computed Properties

Real Example: User Profile Management

I built a user profile system with validation and business rules:

Information Hiding

Exposing Only What's Needed

Internal State Management

Real Example: Shopping Cart

Private Fields (#) - ES2022 Feature

TypeScript also supports JavaScript's private fields using #:

Difference from private:

  • TypeScript private: Compile-time only (becomes public in JavaScript)

  • JavaScript #: Runtime enforcement (truly private)

I prefer private for most cases, # when runtime privacy is critical.

Best Practices

1. Make Everything Private by Default

2. Use Getters for Computed Values

3. Validate in Setters

4. Immutable Objects

What's Next

You now understand:

  • Public, private, and protected access modifiers

  • Getters and setters for controlled access

  • Information hiding and encapsulation

  • Protecting data integrity through validation

  • Computed properties

In Part 3: Inheritance and Polymorphism, you'll learn how to create class hierarchies, extend behavior, override methods, and leverage polymorphism for flexible, maintainable code.


Based on building secure TypeScript applications with proper encapsulation, from banking systems to e-commerce platforms.

Last updated