Access Modifiers in TypeScript: Best Practices for Encapsulation and Data Hiding
Tags: webdev, javascript, typescript, programming
Published: 2024-05-19 00:52:42

While working on a project and creating classes, I faced a dilemma about which access modifiers to use: `public`? `private`? `static`? `public static`? or `private static`? When should `private static` be used?

In this article, I'll discuss each type of access modifier and its usage.
Access modifiers are an essential part of Object-Oriented Programming, but I'll focus on their usage in TypeScript.

**public**
This is the default, or we can explicitly write `public`.
All public members are accessible from anywhere without any restrictions.

```
class User {
    username?: string;
    public email?: string;
}

let user = new User();
user.username = "John";
user.email = "xyz@example.com";
```

**private**
Class members are only visible within the class and are not accessible outside of it.

```
class User {
    public username?: string;
    private email?: string;
}

let user = new User();
user.username = "John";
user.email = "xyz@example.com";  // Error: Property 'email' is private and only accessible within class 'User'.
```

**static**

> Static properties cannot be directly accessed on instances of the class. Instead, they're accessed on the class itself.
[MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static)

```
class User {
  static username = "John";
  static email = "xyz@example.com";

  static staticMethod(){
    return "staticMethod is called";
  }
}

console.log(User.username); // "John" 
console.log(User.email); // "xyz@example.com" 
console.log(User.staticMethod()) // "staticMethod is called" 

let user = new User()
console.log(user.username) // Error
```

_Error: user.username_ 
**JavaScript**:  Returns `undefined` because the property does not exist on the instance
**TypeScript**: Gives a compile-time error

**When is `static` used?**
According to MDN,

> Static methods are often utility functions, such as functions to create or clone objects, whereas static properties are useful for caches, fixed-configuration, or any other data you don't need to be replicated across instances.

Here are the use cases:

**Constant Values**
Provide a single source of truth for constants 

```
class MathConstant {
  static pie = 3.14;
}
console.log(MathConstant.pie);
```

**Utility Functions**
Perform common operations or calculations that are broadly useful and don't depend on instant-specific data

```
class MathUtils {
  static add(a: number, b: number){
    return a + b;
  }
}

console.log(MathUtils.add(5, 3));
```

**Cache for Data**
Share a common cache across all instances to avoid redundant data storage

```
class CacheData {
  static cache: {[key: string]: string} = {};

  static set(key: string, value: string){
    this.cache[key] = value;
  }

  static get(key: string): string | undefined {
    return this.cache[key];
  }
}
```

**Configuration Settings**
Maintain a single source of configuration that can be accessed and modified globally

```
class Config {
  static settings: { [key: string]: string } = {
    theme: 'dark',
    lang: 'en'
  }

  static updateSettings(key: string, value: string){
    this.settings[key] = value;
  }
}
```

**Unique Identifier Generator**
Ensure unique identifiers across the application 

```
class UniqueID {
  static ID = 0;

  static generateID(){
    return ++this.ID;
  }
}
```

**private static**
While public static members are accessible both within the class and outside the class using the class name, private static members are only accessible within the class where they are declared. Private static members are not accessible outside the class or from its subclasses.
The main purpose of private static is **encapsulation** and **data hiding**.

```
class Count {
  private static count: number = 0;

  static increment(){
// Either approach works
    // this.count++; 
    Count.count++;
  }

  static getCount(){
// Either approach works
    // return this.count;
    return Count.count;
  }
}

Count.increment();
Count.increment();
Count.increment();

console.log(Count.getCount()); // 3
```

In this small example, public static might produce the same result, but it violates the encapsulation.

Access modifiers are fundamental concepts that I learned when I started programming. However, as a project grows larger and its structure needs to be well-considered, I need to think through the details more thoroughly.