Access Modifiers: Public, Private, Protected

Table of Contents

  • Introduction
  • Understanding Access Modifiers in TypeScript
    • What are Access Modifiers?
    • Types of Access Modifiers
  • Public Access Modifier
    • Default Behavior
    • Example of Public Access Modifier
  • Private Access Modifier
    • How It Works
    • Example of Private Access Modifier
  • Protected Access Modifier
    • When to Use Protected
    • Example of Protected Access Modifier
  • Access Modifiers in Inheritance
    • Inherited Members and Access Modifiers
  • Conclusion

Introduction

TypeScript, as a statically typed superset of JavaScript, introduces several powerful features to enhance code clarity, maintainability, and scalability. One of the fundamental features is the ability to define access modifiers for class members. Access modifiers control the visibility and accessibility of class members (properties and methods) from outside the class. These modifiers help to implement encapsulation — one of the core principles of object-oriented programming (OOP).

In this article, we will explore the three main access modifiers in TypeScript: public, private, and protected. We will also discuss how these modifiers can be applied in real-world scenarios, along with examples to illustrate their usage.


Understanding Access Modifiers in TypeScript

What are Access Modifiers?

Access modifiers are keywords that define the visibility of class members. They determine whether properties and methods can be accessed from outside the class or whether they are restricted to the class or its subclasses. TypeScript provides three main access modifiers:

  1. Public: Members are accessible from anywhere.
  2. Private: Members are only accessible within the class.
  3. Protected: Members are accessible within the class and its subclasses.

Access modifiers allow you to implement encapsulation, a key concept in object-oriented programming, which helps to protect the internal state of objects and restrict access to critical data.


Types of Access Modifiers

1. Public Access Modifier

The public access modifier is the default for all members in a TypeScript class. Public members can be accessed from anywhere, both inside and outside the class. This is useful when you want a property or method to be freely accessible by other parts of your program.

Default Behavior

In TypeScript, if no access modifier is specified for a member, it is automatically treated as public.

Example of Public Access Modifier

class Car {
public make: string;
public model: string;

constructor(make: string, model: string) {
this.make = make;
this.model = model;
}

public displayDetails(): void {
console.log(`Car Make: ${this.make}, Model: ${this.model}`);
}
}

const car = new Car("Toyota", "Corolla");
console.log(car.make); // Accessible from outside the class
console.log(car.model); // Accessible from outside the class
car.displayDetails(); // Accessible from outside the class

In this example, both make, model, and the displayDetails() method are marked as public. These can be accessed directly from the car object.


2. Private Access Modifier

The private access modifier restricts access to members, making them accessible only within the class that defines them. Private members cannot be accessed or modified from outside the class, even by instances of the class.

This is useful when you want to hide the internal details of a class and only expose a public API for interaction.

How It Works

Private members are not visible to outside code, even if you try to access them through an object instance.

Example of Private Access Modifier

class BankAccount {
private balance: number;

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

public deposit(amount: number): void {
if (amount > 0) {
this.balance += amount;
}
}

public getBalance(): number {
return this.balance;
}
}

const account = new BankAccount(1000);
account.deposit(500);
// account.balance = 2000; // Error: Property 'balance' is private and only accessible within class 'BankAccount'.
console.log(account.getBalance()); // Output: 1500

In this example, balance is a private member of the BankAccount class. You cannot directly access or modify balance from outside the class. The getBalance() method is provided to access the value of balance.

Attempting to access balance directly will result in a compile-time error.


3. Protected Access Modifier

The protected access modifier is similar to private, but with a key difference: protected members are accessible within the class and its subclasses (derived classes). This makes the protected modifier particularly useful when you want to share some functionality with subclasses but hide it from external code.

When to Use Protected

Use the protected modifier when you want to allow subclasses to have access to certain class members while keeping them hidden from outside code.

Example of Protected Access Modifier

class Animal {
protected name: string;

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

protected speak(): void {
console.log(`${this.name} makes a sound.`);
}
}

class Dog extends Animal {
constructor(name: string) {
super(name);
}

public bark(): void {
console.log(`${this.name} barks.`);
}
}

const dog = new Dog("Rex");
dog.bark(); // Output: Rex barks
// dog.speak(); // Error: Property 'speak' is protected and only accessible within class 'Animal' and its subclasses.

In this example:

  • name and speak() are protected in the Animal class.
  • Dog, which extends Animal, can access name and speak() because they are marked as protected.
  • However, we cannot access speak() directly from an instance of Dog or from outside the class hierarchy.

Access Modifiers in Inheritance

When a class inherits from another class, the access modifiers on the parent class members dictate how those members can be accessed in the child class.

Inherited Members and Access Modifiers

  • Public members of the parent class can be accessed directly from the child class or outside code.
  • Private members are not accessible in the child class.
  • Protected members can be accessed within the child class but not outside of it.

Example: Inheritance with Access Modifiers

class Vehicle {
public model: string;
private year: number;
protected speed: number;

constructor(model: string, year: number, speed: number) {
this.model = model;
this.year = year;
this.speed = speed;
}

public accelerate(): void {
this.speed += 10;
}

private changeYear(newYear: number): void {
this.year = newYear;
}

protected getSpeed(): number {
return this.speed;
}
}

class Car extends Vehicle {
constructor(model: string, year: number, speed: number) {
super(model, year, speed);
}

public displaySpeed(): void {
console.log(`The car is moving at ${this.getSpeed()} km/h.`);
}
}

const car = new Car("Honda", 2021, 100);
console.log(car.model); // Accessible (public)
car.accelerate(); // Works because accelerate is public
// car.year = 2022; // Error: Property 'year' is private and only accessible within class 'Vehicle'.
// car.getSpeed(); // Error: Property 'getSpeed' is protected and only accessible within class 'Vehicle' and its subclasses.

In this example:

  • model is public and can be accessed both inside and outside the class.
  • year is private and cannot be accessed or modified directly by the Car class or its instances.
  • speed is protected and is accessible inside Car because it extends Vehicle.

Conclusion

Access modifiers are essential in TypeScript for enforcing encapsulation and ensuring that the internal state of a class is protected. By using public, private, and protected, you can control how class members are accessed and prevent unintended modifications.

  • Public members are freely accessible from anywhere.
  • Private members are only accessible within the class.
  • Protected members are accessible within the class and its subclasses.

Using access modifiers effectively helps to build more maintainable and secure applications by restricting access to sensitive or critical data and functions. They are a powerful tool to manage class design and inheritance in TypeScript.