Class Public and Private Access Modifiers in TypeScript

Updated: January 7, 2024 By: Guest Contributor Post a comment

Overview

TypeScript, an extension of JavaScript, offers access modifiers to control the visibility of class members. This tutorial dives into the use of public and private keywords to enhance encapsulation in your object-oriented code.

Introduction to Access Modifiers

Access modifiers in TypeScript are keywords that set the accessibility of properties and methods within a class. TypeScript provides three main access modifiers:

  • Public: This is the default modifier; members are accessible everywhere.
  • Private: Members are only accessible within the class they’re declared in.
  • Protected: Members are accessible within the declaring class and any subclass.

Let’s start with public and private modifiers, which are crucial for encapsulation.

Basic Usage of Public and Private

class Animal {
  public name: string;
  private species: string;

  constructor(name: string, species: string) {
    this.name = name;
    this.species = species;
  }

  public describe(): string {
    return `${this.name} is a ${this.species}`;
  }

  private logSpecies(): void {
    console.log(this.species);
  }
}

const dog = new Animal('Rex', 'Canine');
console.log(dog.describe()); // Rex is a Canine
dog.logSpecies(); // Error: 'logSpecies' is a private method.

In the example above, name is public and accessible anywhere, whereas species and logSpecies are private and we get an error trying to access logSpecies from outside the class.

Advanced Utilization of Private Modifiers

Private access can also be used to safeguard against method overriding and to encapsulate complex logic that shouldn’t be exposed:

class Bird extends Animal {
  constructor(name: string) {
    super(name, 'Aves');
  }

  describe(): string {
    // We can still call describe() from the parent class as it's public
    return `${super.describe()} and loves to fly.`;
  }

  // We cannot override logSpecies here, as it's private in Animal
}

const parrot = new Bird('Rio');
console.log(parrot.describe()); // Rio is a Aves and loves to fly.

Notice the logSpecies() method from the Animal class cannot be overridden as it is private.

Using Access Modifiers in Constructors

One powerful feature in TypeScript is the ability to define class member accessibility directly within the constructor parameters:

class Plant {
  constructor(public name: string, private species: string) {}

  public display(): void {
    console.log(`${this.name} is part of the ${this.species} species.`);
  }
}

const sunflower = new Plant('Sunflower', 'Helianthus');
sunflower.display(); // Sunflower is part of the Helianthus species.

This syntax not only defines the access level but also automatically assigns the parameter to a class member.

Encapsulation in Action

Now let’s put our knowledge to work and create a class that uses both public and private access modifiers to manipulate and store data securely:

class BankAccount {
  public readonly accountNumber: string;
  private balance: number;

  constructor(accountNumber: string, initialBalance: number) {
    this.accountNumber = accountNumber;
    this.balance = initialBalance;
  }

  public deposit(amount: number): void {
    this.balance += amount;
    console.log(`Deposit successful. New balance: ${this.balance}`);
  }

  private deductFees(feeAmount: number): void {
    this.balance -= feeAmount;
    console.log(`Fees applied. New balance: ${this.balance}`);
  }

  public withdraw(amount: number): void {
    if (amount + 30 > this.balance) { // assuming a flat fee of 30 for simplicity
      console.log('Insufficient funds.');
      return;
    }
    this.deductFees(30);
    this.balance -= amount;
    console.log(`Withdrawal successful. New balance: ${this.balance}`);
  }
}

const account = new BankAccount('12345', 500);
account.deposit(100); // Deposit successful. New balance: 600
account.withdraw(200); // Withdrawal successful. New balance: 370

This example demonstrates encapsulation via private methods to handle internal processes while exposing only the necessary functionality to the user.

Conclusion

Understanding and correctly implementing public and private access modifiers in TypeScript is critical for creating robust, encapsulated, and maintainable code. Through the examples provided, we’ve seen the control these modifiers grant us, enforcing a strong contract for other parts of the code that interact with our classes.