Should I write methods as arrow functions in Angular’s class

The points made in this React answer are still valid in Angular, any other framework or vanilla JavaScript/TypeScript.

Class prototype methods are ES6, class arrow methods aren’t. Arrow methods belong to class fields proposal and not a part of existing specs. They are implemented in TypeScript and can be transpiled with Babel as well.

It’s generally preferable to use prototype method() { ... } than arrow method = () => { ... } because it’s more flexible.

Callbacks

The only real opportunity that arrow method provides is that it it can be seamlessly used as a callback:

class Class {
  method = () => { ... }
}

registerCallback(new Class().method);

If prototype method should be used as a callback it should be additionally bound, this should be preferably be done in constructor:

class Class {
  constructor() {
    this.method = this.method.bind(this);
  }

  method() { ... }
}

registerCallback(new Class().method);

A decorator like bind-decorator can be used in TypeScript and ES Next to provide more concise alternative to method binding in constructor:

import bind from 'bind-decorator';

class Class {
  @bind
  method() { ... }
}

Inheritance

Arrow method restricts child classes to use arrow methods too, otherwise they won’t be overridden. This creates a problem if an arrow was overlooked:

class Parent {
  method = () => { ... }
}

class Child extends Parent {
  method() { ... } // won't override Parent method
}

It’s not possible to use super.method() in child class because super.method refers to Parent.prototype.method, which doesn’t exist:

class Parent {
  method = () => { ... }
}

class Child extends Parent {
  method = () => {
    super.method(); // won't work
    ...
  }
}

Mixins

Prototype methods can be efficiently used in mixins. Mixins are useful for multiple inheritance or to fix problems in TypeScript method visibility.

Since arrow method isn’t available on class prototype, it can’t be reached from outside the class:

class Parent {
  method = () => { ... }
}

class Child extends OtherParent { ... }
Object.assign(Child.prototype, Parent.prototype) // method won't be copied

Testing

A valuable feature that prototype methods provide is that they are accessible before class instantiation, thus they can be spied or mocked in tests, even if they are called right after construction:

class Class {
  constructor(arg) {
    this.init(arg);
  }

  init(arg) { ... }
}

spyOn(Class.prototype, 'init').and.callThrough();
const object = new Class(1);
expect(object.init).toHaveBeenCalledWith(1);

This is not possible when a method is an arrow.

TL;DR: the choice between prototype and arrow class methods seems like a matter of taste, but in reality the use of prototype methods is more far-sighted. You may usually want to avoid arrow class methods, unless you are sure that they will cause no inconvenience. Don’t forget to use bind on prototype methods if you pass them as callbacks.

Leave a Comment