lookout.devlookout.dev
search
Share Knowledge
00

Protect Class Members and Expose Only the Public API

Wednesday, January 22, 2020

This best practice is at the core of object-oriented programming's (OOP) concept that "classes are open for extension but closed for modification". This best practice will focus on the TypeScript implementation but is applicable broadly to (almost) any programming language.

In TypeScript we can use the public, protected and private modifiers for class members (both properties and methods) to specify if the member is publicly available to consumers of the class, only available to extensions of the class, or even more strict, only available within the class.

Public by Default

The default access level of a class member in TypeScript is public. When we omit the modifier, then the class member is publicly available. And this might not be what our intention is. We do not want to expose the internal workings of our class, and we want to be able to modify the internal working as we see fit in the future without breaking changes to the consumers of our classes.

Private is Good

Privacy is a good thing, not only in terms of Internet privacy, but certainly in terms of the internal details and implementations of our classes. As such, it is recommended that all class members, both properties and methods, use the private access modifier unless the member must be exposed to the consumers of our class, and therefore making it part of the public interface of the class.

Protected for Extension

The protected access modifier is used when we want to expose a class member to extending (or inheriting) classes. Class inheritance is incredibly powerful and should be implemented when abstraction and implementation differ.

Instructions

checkmark-circle
Do

use the protected access modifier for class members that must be exposed to inheriting classes

use the private access modifier for class members unless the member must be exposed to inheriting classes or to consumers of the class

error-circle
Avoid

omitting the access modifier and exposing all class members publicly

the protected access modifier until extension (inheritance) becomes necessary

question-circle
Consider

using the private access modifier by default

info-circle
Why

classes are open to extension but closed for modification

public class members are vulnerable to breaking changes

future modification to private class members avoids breaking changes to the public API

Code Examples

specify access modifiers for class members

class UserProfile extends Component<AppProps, AppState> {
  
  constructor(private readonly props) {
    super(props);
  }

  private save(user: Partial<User>): void {
    // code omitted for brevity
  }
  
  render() {
    return (
      <>
        <Avatar user={this.props.user} />
        <UserForm user={this.user} onSave={user => this.save(user)} />
      </>
    );
  }
}

exposing all class members publicly

class UserProfile extends Component<AppProps, AppState> {
  
  constructor(props) {
    super(props);
  }

  save(user: Partial<User>): void {
    // code omitted for brevity
  }
  
  render() {
    return (
      <>
        <Avatar user={this.props.user} />
        <UserForm user={this.user} onSave={user => this.save(user)} />
      </>
    );
  }
}
Brian Love

I am a software engineer and Google Developer Expert in Web Technologies and Angular with a passion for learning, writing, speaking, teaching and mentoring. I regularly speaks at conferences and meetups around the country, and co-authored "Why Angular for the Enterprise" for O'Reilly. When not coding, I enjoy skiing, hiking, and being in the outdoors. I started lookout.dev to break down the barriers of learning in public. Learning in public fosters growth - for ourselves and others.

Google Developers Expert

Have a question or comment?

Brian Love
Brian Love ·

How does this work with the new TC39 proposal for private fields?