Remove argument-order dependency and provide defaults

Argument order in function signature is a common source of bugs and frustration. Imagine you have the following Vehicle class, its constructor accepts values to instantiate an object. Passing the values in the wrong order can easily lead to bugs that are not so easy to catch.

class Vehicle {
  constructor(speed, weight, power, color, brand){
    this.speed = speed;
    this.weight = weight;
    this.power = power;
    this.color = color;
    this.brand = brand;
  }
}

Obviously we can avoid the order dependency by passing an object to the function.

class Vehicle {
  constructor(args = {}){
    Object.keys(args)
      .forEach(argument => this[argument] = args[argument]);
  }
}

There is a problem though, what if we want our constructor to have some default values in case these are not provided? In the first example, we would write our class like that:

class Vehicle {
  constructor(speed = 100, weight = 1500, power = 111, color = "red", brand = "Kia"){
    this.speed = speed;
    this.weight = weight;
    this.power = power;
    this.color = color;
    this.brand = brand;
  }
}

To provide defaults in case we use an object as a single parameter, we can create a defaults property that returns our defaults, and merge the passed args object with our defaults in the constructor. The defaults property acts also as a form of documentation for the properties our class expects.

class Vehicle {
  constructor(args = {}){
    args = { ...this.defaults, ...args };
    Object.keys(args)
      .forEach(argument => this[argument] = args[argument]);
  }
 
  get defaults(){
    return {
      speed: 100,
      power: 1000
    }
  }
}

Subclasses can provide their own defaults

class Car extends Vehicle {
  get defaults(){
    return {
      speed: 150,
    }
  }
}

Or even extend the defaults provided by the base class

class Car extends Vehicle {
  get defaults(){
    return {
      ...Vehicle.prototype.defaults,
      speed: 150,
    }
  }
}
Published 9 Sep 2018

Engineering Manager. Opinions are my own and not necessarily the views of my employer.
Avraam Mavridis on Twitter