1

What is the best way to generalize the setters and getters in this class:

class A {
    constructor() {
        this._foo = new Foo();
        this._bar = new Bar();
    }

    get foo() {
        return this._foo;
    }

    set foo(value) {
        if (value instanceof Foo)
            this._foo = value;
        else
            this._foo = Object.assign(new Foo(), value);
    }

    get bar() {
        return this._bar;
    }

    set bar(value) {
        if(value instanceof Bar)
            this._bar = value;
        else
            this._bar = Object.assign(new Bar(), value);
    }
}

Edit

Yes, the question can be opinion based and can be get around with a typed language. But how to resolve it in es6 for existing projects without migration option?

I need this setters to define the type of members after deserializing a json document saved in database :

{
    "foo" :{"x":0,"y":0,"z":0},
    "bar" : {"propA": "valueA", "propB": "valueB"}
}
Troopers
  • 5,127
  • 1
  • 37
  • 64
  • Don't have them at all and use some sort of type system (typescript, flow ...) – Jonas Wilms Feb 05 '19 at 14:53
  • 5
    I'm not sure what you mean by "typed" or what you mean by "generalize" ... – Pointy Feb 05 '19 at 14:53
  • 4
    Getting trigger happy with the close votes eh? This post can be **easily** salvaged by asking the OP for clarification. Chill out people. – nicholaswmin Feb 05 '19 at 14:55
  • are you trying to do something [like that](https://stackoverflow.com/questions/7891937/is-it-possible-to-implement-dynamic-getters-setters-in-javascript)? – Kaddath Feb 05 '19 at 15:02
  • What do you want to achieve? Are you trying to have an automated "template" for the `setX` that runs the same logic whenever called (check if parameter is of correct type, act based on the result)? – VLAZ Feb 05 '19 at 15:05

2 Answers2

3

You could theoretically use a mixin:

 const Typed = (key, type, parent = class {}) => class Typed extends parent {
   constructor(...props) {
    super(...props);
     this[`_${key}`] = new type();
   }

  get [key]() { return this[`_${key}`]; }

  set [key](value) { 
      this[`_${key}`] = value instanceof type ? value : Object.assign(new type, value);
  }
}

const A = Typed("foo", Foo, Typed("bar", Bar, class {
 //...
});

But you should probably not use getter / setters at all but rather fix the code that tries to set the property with invalid values.

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • 2
    I wouldn't say the code that sets "invalid values" is necessarily broken. I can see a good reason to have it. Then again, I don't know if this is a design decision taken or a post-factum bugfix applied. If it's the latter, then the solution is indeed to fix it and maybe even introduce type checks into the system. – VLAZ Feb 05 '19 at 15:11
  • I need to set the type of members after deserializing a json document saved in a database – Troopers Feb 05 '19 at 15:22
2

If you wanted to abstract the creation of the setters/getters, you can of course write a function to do that:

function typedAccessor(obj, name, type, internal) {
    Object.defineProperty(obj, name, {
        get() {
            return this[internal];
        },
        set(val) {
            if (val instanceof type)
                this[internal] = val;
            else
                this[internal] = Object.assign(new type, val);
        }
    });
}

class A {
    constructor() {
        this._foo = new Foo();
        this._bar = new Bar();
    }
}
typedAccessor(A.prototype, "foo", Foo, "_foo");
typedAccessor(A.prototype, "bar", Bar, "_bar");

I would however recommend to avoid this pattern.

I need this setters to define the type of members after deserializing a json document

Better use a custom static method that knows how to deal with JSON representations than doing Object.assign (and hope that everything has proper setters). This gives you much better control over the serialisation/deserialisation process and makes your class simpler, with less boilerplate code.

class A {
    constructor(foo, bar) {
        this.foo = foo;
        this.bar = bar;
    }
    static fromJSON(val) {
        return new this(Foo.fromJSON(val.foo), Bar.fromJSON(val.bar);
    }
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375