Ethan Leyden
Ethan Leyden

Reputation: 298

How do I ensure that each and every argument was passed to a constructor in JS

I have the following two classes, where Circle is an extension of Entity. The constructor for Entity uses a single argument which is supposed to be an Object, in order to allow customization of a new Entity. Due to the more specific nature of the Circle, I created a constructor with specific arguments, which I want to be less flexible. Here are the class declarations:

class Entity {
  constructor(e) {
    //use the attributes in the passed object to create a new Entity.
    this.x = e.x; //position
    this.y = e.y;
    this.vY = this.vX = 0; //velocity
    this.aX = this.aY = 0; //acceleration

  }
}
class Circle extends Entity {
  constructor(x, y, r, color) {
    super({"x":x,"y":y}); //create Entity with given position;
    this.r = r;
    this.color = color;
  }
}

I have also written a function addEntity(entity) which is defined as follows:

addEntity(e) {
    var ent_properties = Object.keys(e);
    if(e.type) { //create a specific predefined Entity type, if thats what you want!
      switch(e.type) {
        case "circle":
          try {
            this.entities.push(new Circle(e.x, e.y, e.r, e.color));
          } catch (err) { console.error(err); }
          break;
        default:
          console.error("Invalid Entity Type Created: ");
          console.error(e);
      }
    }
  }

Obviously, there is still a good amount of work to be done here, and a lot of context that I left out (which isn't exactly relevant to the question at hand, but my issue that I bring to everyone here is this:

If I were to call

e.addEntity({
      "type": "circle",
      "x": x,
      "y": y,
      "r": 100,
      "color": "black"
    });

This creates a valid object, with every relevant value filled. However, let's say that I forgot to include "r", or instead of writing "r", I wrote "radius", what would be the best way to catch that? As of right now, it just creates a Circle with this.r = undefined.

Additionally, what is the best way to make sure that the object I give to addEntity has all the necessary attributes, depending on the type of Entity that I want to create? I want to be able to add MORE types, while also making sure that if I were to add those types, I could throw an error if the type of Entity was not define correctly.

TL;DR: How do I quickly validate in my code that a constructor was run using all of the necessary arguments?

Upvotes: 0

Views: 29

Answers (1)

CertainPerformance
CertainPerformance

Reputation: 370769

Iterating over an array of required properties and then checking hasOwnProperty would work...

const requiredProps = ['type', 'x', 'y', 'r', 'color'];
// ...

// Test the argument:
if (requiredProps.some(prop => !e.hasOwnProperty(prop))) {
  throw new Error('Required prop not found');
}

Or perhaps have baseProps for the Entity, and additional props for other subclasses:

const baseRequiredProps = ['type', 'x', 'y', 'r'];

// ...
class Entity {
  constructor(e) {
    const requiredProps = [...baseRequiredProps, ...(this.constructor.additionalRequiredProps || [])]
    if (requiredProps.some(prop => !e.hasOwnProperty(prop))) {
      throw new Error('Required prop not found');
    }

// ...
class Circle extends Entity {
  static additionalRequiredProps = ['color'];

In larger applications, I'd recommend approaching worries like this by using TypeScript instead, to force the caller to provide arguments of the desired type, without requiring any additional runtime code. It does take some getting used to, but it makes maintaining code a whole lot easier.

class Entity {
  x: number;
  y: number;
  constructor(e: Entity) {
      this.x = e.x;
      this.y = e.y;
  }
}
// works:
const e = new Entity({ x: 5, y: 10 });

class Circle extends Entity {
    type = 'circle';
    r: number;
    color: string;
    constructor(e: Circle) {
        super(e);
        this.type = e.type;
        this.r = e.r;
        this.color = e.color;
    }
}
// fails:
const c = new Circle({
    x: 5,
    y: 7,
    radius: 12,
    color: 'black',
    type: 'circle'
});

Since radius was accidentally typed instead of r, you get an error:

enter image description here

Upvotes: 1

Related Questions