Answer a Question
Sign In

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

  • Do:

    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

  • Why:

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

Avoid

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);
  }
}

Do

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);
  }
}
Brian Love
Brian is a software engineer and Google Developer Expert in Web Technologies and Angular with a passion for learning, writing, speaking, teaching and mentoring. He regularly speaks at conferences and meetups around the country, and co-authored "Why Angular for the Enterprise" for O'Reilly. When not coding, Brian enjoys skiing, hiking, and being in the outdoors. Brian recently launched lookout.dev where you can find best practices and expert advice on topics ranging from TypeScript, Angular, React, Node.js and more.
Google Developers Expert

Whoa 🤚 You need to sign in to join the discussion.

Don't worry, if you start a comment, we'll save it for when you return. 😉