Protect Class Members and Expose Only the Public API
Wednesday, January 22, 2020
Protect Class Members and Expose Only the Public API
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
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
omitting the access modifier and exposing all class members publicly
the protected access modifier until extension (inheritance) becomes necessary
using the private access modifier by default
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)} />
</>
);
}
}
Have a question or comment?
How does this work with the new TC39 proposal for private fields?