00

Specify Access Modifiers for TypeScript Class Members

Monday, November 11, 2019

It is important to understand and to specify access modifiers for TypeScript class members.

First, public is the default access. That means that any properties or methods in your class that do not specify an access modifier are publicly available to consumers of the class. A public property or method should be considered the API to the class, and ideally, should not be open for modification.

Private properties and methods use the private access modifier prior to the property or method name. This denotes that the property or method is internal to the class, and should not be accessed by consumers of the class. This also means that the private property and method names and signatures can be modified as necessary.

Protected properties and methods use the protected access modifier prior to the property or method name. This denotes that the property or method is internal to the class, as well as all classes that extend, and thus inherit the parent class's functionality. The protected access modifier enables developers to build classes that are closed for modification, but are open to extension. Specific implementations and future feature classes can extend the class to modify the behavior of the class, or to add additional functionality.

It is important to not simply leave off access modifiers for all class members, as this leaves the class surface open to consumers of the class to expect that all properties and methods are stable and will not be modified in the future.

The key takeaway is: only leave class members open to the public if they are intentionally meant for the public. And, if the class member is intended to be public, you can use the shorthand syntax or not specifying the access modifier since public is the default.

Instructions

Do

not specify the access modifier when the class member is public

specify either the private or protected access modifier as necessary

Avoid

leaving all class members as public

Consider

starting with the private access modifier for class members that may not necessarily be publicly accessible, and then implement protected on a case-by-case basis

Why

classes should be closed for modification but open for extension

defaulting all class members to public could expose properties and methods to consumers that are not intended to be used

Code Examples

Specify the private and protected access modifiers

import { Contribution } from './contribution.model';
import { ContributionService } from '../services/contribution.service';

export class User {
  /** Get the number of contributions for this user. */
  public get contributionCount(): number {
    return this.getContributions().length;
  }

  /** The user-friendly display name of the user. */
  public displayName: string;

  /** The user's email address. */
  public email: string;

  /** Access details. */
  private access: { admin: boolean, editor: boolean };

  constructor(data: { [ key: string]: any }) {
    Object.assign(this, data);
  }

  /** Returns true if the user can edit contributions. */
  public canEdit(): boolean {
    return this.access && (this.access.admin || this.access.editor);
  }

  /** Get the contributions for the current user. */
  protected getContributions(): Contribution[] {
    return ContributionService.contributionsByUser(this);
  }
}

Prefer short-hand syntax and drop the public access modifiers

import { Contribution } from './contribution.model';
import { ContributionService } from '../services/contribution.service';

export class User {
  /** Prefer short-hand syntax and drop the `public` access modifiers. */
  public get contributionCount(): number {
    return this.getContributions().length;
  }

  /** Prefer short-hand syntax and drop the `public` access modifiers. */
  public displayName: string;

  /** Prefer short-hand syntax and drop the `public` access modifiers. */
  public email: string;

  /** Access details. */
  private access: { admin: boolean, editor: boolean };

  constructor(data: { [ key: string]: any }) {
    Object.assign(this, data);
  }

  /** Prefer short-hand syntax and drop the `public` access modifiers. */
  public canEdit(): boolean {
    return this.access && (this.access.admin || this.access.editor);
  }

  /** Get the contributions for the current user. */
  protected getContributions(): Contribution[] {
    return ContributionService.contributionsByUser(this);
  }
}

All class members are public

import { Contribution } from './contribution.model';
import { ContributionService } from '../services/contribution.service';

export class User {
  /** Prefer short-hand syntax and drop the `public` access modifiers. */
  get contributionCount(): number {
    return this.getContributions().length;
  }

  /** Prefer short-hand syntax and drop the `public` access modifiers. */
  displayName: string;

  /** Prefer short-hand syntax and drop the `public` access modifiers. */
  email: string;

  /** Access details. */
  access: { admin: boolean, editor: boolean };

  constructor(data: { [ key: string]: any }) {
    Object.assign(this, data);
  }

  /** Prefer short-hand syntax and drop the `public` access modifiers. */
  public canEdit(): boolean {
    return this.access && (this.access.admin || this.access.editor);
  }

  /** This should be private to the class. */
  getContributions(): Contribution[] {
    return ContributionService.contributionsByUser(this);
  }
}
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

Discussions are healthy ❤️