yantrab
yantrab

Reputation: 2672

Object assigned in base class does not change the child class

I want to create a base class with initial data like below:

export abstract class Entity {
    constructor(data?) {
        if (data) {
            Object.assign(this, data);
        }
    }
}

with a child class like below:

export class Filter extends Entity{
      show = true
      constructor(filter?: Partial<Filter>) {
        super(filter);
    }
}

The issue I am facing is that when I create an object like this new Filter({show:false}), I get the following result:

Filter {show: true}

stackblitz

The object in base class did not reflect the values in child class. Any thoughts why is this happening?

Upvotes: 6

Views: 1317

Answers (3)

Darkerc
Darkerc

Reputation: 19

I solved by adding a setTimeOut on the Object.assing like this:

constructor (data) {
  setTimeout(() => {
    Object.assing(this, data)
  }, 0)
}

It makes the assing at the finish of the javascript queue. Or you can make a class decorator and do your logic overriding the constructor

function DecoratorName () {
  return function _DecoratorName<T extends {new (...args: any[]): {}}>(constr: T) {
    return class extends constr {
      constructor (...args: any[]) {
        // The logic inside here executes after child declaration 
        // and allows get no undefined on properties on 
        // Object.assing use
        super(...args)
        Object.assign(this, args[0].data)
      }
    }
  }
}

@DecoratorName()
class Example {

}

Upvotes: -1

briosheje
briosheje

Reputation: 7446

This happens because, once the code is transpiled, the child class property assignment happens after the parent constructor has been invoked, you can see that by using the typescript playground and set, in the options, to target ES5.

To make it shorter, the transpiled code will be the following:

"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
var Entity = /** @class */ (function () {
    function Entity(data) {
        if (data) {
            Object.assign(this, data);
        }
    }
    return Entity;
}());
exports.Entity = Entity;
var Filter = /** @class */ (function (_super) {
    __extends(Filter, _super);
    function Filter(filter) {
        var _this = _super.call(this, filter) || this;
        _this.show = true;
        return _this;
    }
    return Filter;
}(Entity));
exports.Filter = Filter;
new Filter({ show: false });

As you can see, the relevant part is that, in the Filter's contructor, the show property is assigned after the parent constructor has been executed.

var Filter = /** @class */ (function (_super) {
    __extends(Filter, _super);
    function Filter(filter) {
        var _this = _super.call(this, filter) || this;
        _this.show = true;
        return _this;
    }
    return Filter;
}(Entity));

To solve the issue, you should just redesign your code and make sure you assign the property in the constructor, instead in the class declaration, leaving the class declaration empty:

export abstract class Entity {
    constructor(data?) {
        if (data) {
          Object.assign(this, data);
        }
    }
}

export class Filter extends Entity{
      public show: boolean;
      constructor(filter?: Partial<Filter>) {
        super(filter);
        this.show = (this.show === undefined) ? true : this.show;
        console.log(this);
    }
}

new Filter({show:false}); // show is false
new Filter(); // show is true (default value)

Working code: https://stackblitz.com/edit/typescript-c4u557

Another possible approach is that Entity class should have a show property with a default value, but it doesn't seems like you want do that, from the hierarchy you've shown.

Upvotes: 8

StepUp
StepUp

Reputation: 38174

It happens because you've set show variable to true in your derived class. So it overrides the value of base class. To avoid this behavior you should just delete initialization value:

export class Filter extends Entity{
      show;
      constructor(filter?: Partial<Filter>) {
        super(filter);
        console.log('Filter', this)
    }
}

Upvotes: 0

Related Questions