Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript: object.hasOwnProperty() shows true on inherited property. Why?

If I understand correctly, object.hasOwnProperty() should return false on inherited properties of the parent class. However, the following code returns true on both own and inherited properties.

Is my understanding/code incorrect or is hasOwnPropery() incorrect? If it's me, how do I discriminate between own and inherited properties?

Edit: I've added my use case to the sample code.

I would expect the child's fromDb() would only take care of its own properties, instead, it overrides the properties set by the parent's fromDb().

class Parent {
    parentProp = '';
    fromDb(row: {}) {
        for (const key of Object.keys(row)) {
            if (this.hasOwnProperty(key)) {
                if (key === 'parentProp') {
                    // Do some required data cleansing
                    this[key] = row[key].toUpperCase()
                } else {
                    this[key] = row[key];
                }
            }
        };
        return this;
    }
}

class Child extends Parent {
    childProp = '';
    fromDb(row: {}) {
        super.fromDb(row);
        for (const key of Object.keys(row)) {
            if (this.hasOwnProperty(key)) {
                this[key] = row[key];
            }
        };
        return this;
    }
}

let row = {
    parentProp: 'parent',
    childProp: 'child',
}

let childObj = new Child().fromDb(row);
console.log(childObj);

Console:

Child:
    childProp: "child"
    parentProp: "parent"
like image 478
passerby Avatar asked Sep 07 '25 12:09

passerby


1 Answers

In the generated extends code, properties are copied to the subclass like this:

function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };

Which means that your subclass (d) is given it's own property.

This is actually no different to using plain JavaScript inheritance:

function Parent() {
    this.parentProp = `I'm defined by Parent`;
}

function Child() {
  Parent.call(this);
  this.childProp = `I'm defined by Child`;
}

let childObj = new Child();

for (const key of Object.keys(childObj)) {
    console.log(key, childObj.hasOwnProperty(key));
}

If you give us some direction on what you need to achieve, I'm sure we'll find a suitable mechanism for you that will overcome this obstacle.

Specific Use Case

For your specific use case, you can set the precedent for who "wins" by where you call the superclass.

To get the output

childProp: "child"
parentProp: "PARENT"

Let the parent run "second", rather than "first":

class Child extends Parent {
    childProp = '';
    fromDb(row: {}) {
        for (const key of Object.keys(row)) {
            if (this.hasOwnProperty(key)) {
                this[key] = row[key];
            }
        };

        super.fromDb(row); // <-- last update wins
        return this;
    }
}

Super Dynamic Property Stuff

This will dynamically exclude parent keys from the child and child keys from the parent... add console.log statements to see the internals...

class Parent {
    parentProp = '';
    fromDb(row: {}) {

        const ownKeys = Object.keys(new Parent());

        for (const key of Object.keys(row)) {
            if (ownKeys.indexOf(key) > -1) {
                if (key === 'parentProp') {
                    // Do some required data cleansing
                    this[key] = row[key].toUpperCase()
                } else {
                    this[key] = row[key];
                }
            }
        };
        return this;
    }
}

class Child extends Parent {
    childProp = '';
    fromDb(row: {}) {
        super.fromDb(row);

        const ownKeys = this.getKeys();

        for (const key of Object.keys(row)) {
            if (ownKeys.indexOf(key) > -1) {
                this[key] = row[key];
            }
        };
        return this;
    }

    getKeys() {
        const childKeys = Object.keys(this);
        const parentKeys = Object.keys(new Parent());

        return childKeys.filter( function( el ) {
             return parentKeys.indexOf( el ) < 0;
        });
    }
}

let row = {
    parentProp: 'parent',
    childProp: 'child',
}

let childObj = new Child().fromDb(row);
console.log(childObj);
like image 157
Fenton Avatar answered Sep 10 '25 07:09

Fenton