Typescript – 无法通过 Object.keys() 访问子类中的属性。

huangapple go评论58阅读模式
英文:

Typescript - can't access properties in child class via Object.keys()

问题

I am trying to use the same logic declared in constructor of base class for all child classes.
In more details, I want to iterate over all class attributes from it's constructor.

My expectation was that if I invoke Object.keys() in base constructor and call super() in child's constructor - it will work for me.

Code example:

class BaseClass {
    private baseField1: string;
    private baseField2: string;

    constructor(data: any) {
        console.log(this.constructor.name + " has fields: " + Object.keys(this));
    }
}

class ChildClass extends BaseClass {
    private childField1: string;

    constructor(data: any) {
        super(data);
    }
}

let base = new ChildClass({name: 'Joe'});

I expect to have output:
ChildClass has fields: baseField1, baseField2, childField1

But in fact I have:
ChildClass has fields: baseField1, baseField2

There is no childField1 in output

Any idea on how this is better to sort out?

UPD:

Finally I made it work as I expect. Thanks to Touffy!

class BaseClass {
    private baseField1: string;
    private baseField2: string;

    constructor(data: any) {
        Object.assign(this, data);
    }
}

class ChildClass extends BaseClass {
    private childField1: string;

    constructor(data: any) {
        super(data);
        if (data.childField1) this.childField1 = data.childField1;
    }
}

let base = new ChildClass({
    baseField1: 'bf1',
    baseField2: 'bf2', 
    childField1: 'Joe'
});

console.log('Class fields: ' + JSON.stringify(base));

Output is: Class fields: {"baseField1":"bf1","baseField2":"bf2","childField1":"Joe"}

英文:

I am trying to use the same logic declared in constructor of base class for all child classes.
In more details, I want to iterate over all class attributes from it's constructor.

My expectation was that if I invoke Object.keys() in base constructor and call super() in child's constructor - it will work for me.

Code example:

class BaseClass {
    private baseField1: string;
    private baseField2: string;

    constructor(data: any) {
        console.log(this.constructor.name + " has fields: " + Object.keys(this));
    }
}

class ChildClass extends BaseClass {
    private childField1: string;

    constructor(data: any) {
        super(data);
    }
}

let base = new ChildClass({name: 'Joe'});

I expect to have output:
ChildClass has fields: baseField1,baseField2,childField1

But in fact I have:
ChildClass has fields: baseField1,baseField2

There is no childField1 in output

Any idea on how this is better to sort out?

UPD:

Finally I made it work as I expect. Thanks to Touffy!

class BaseClass {
    private baseField1: string;
    private baseField2: string;

    constructor(data: any) {
        Object.assign(this, data);
    }
}

class ChildClass extends BaseClass {
    private childField1: string;

    constructor(data: any) {
        super(data);
        if (data.childField1) this.childField1 = data.childField1;
    }
}

let base = new ChildClass({
    baseField1: 'bf1',
    baseFileds2: 'bf2', 
    childField1: 'Joe'
});

console.log('Class fields: ' + JSON.stringify(base));

Output is: Class fields: {"baseField1":"bf1","baseFileds2":"bf2","childField1":"Joe"}

答案1

得分: 1

问题不在于Object.keys,而在于它在构造函数链中的调用时机。你在父构造函数中调用了Object.keys,而这个构造函数是由子构造函数中的super调用的,在初始化任何子属性之前

你需要记住,这些漂亮的属性声明仅仅是语法糖,相当于构造函数中的this.propertyName = undefined。这些行在调用super(父构造函数)之后才会被评估。换句话说,JavaScript解释器实际上会执行以下操作:

class BaseClass {
    constructor(data) {
        this.baseField1 = undefined;
        this.baseField2 = undefined
        console.log(this.constructor.name + " has fields: " + Object.keys(this));
    }
}

class ChildClass extends BaseClass {
    constructor(data) {
        BaseClass.call(this, data);
        this.childField1 = undefined // 太晚了!
    }
}

如果你在子构造函数中super()之后或者在不是构造函数的任何地方调用Object.keys,它会按预期工作。

然而,如果你的目标特别是在子实例被构造时立即记录所有属性并且要从父类继承这种行为…我认为即使使用代理也不能实现。

英文:

The issue is not with Object.keys, but the timing of when it is called in the constructor chain. You wrote the call of Object.keys in the parent constructor, which is called by super in the child constructor before any child properties are initialized.

You need to remember that those nice property declarations are merely syntactic sugar, equivalent to this.propertyName = undefined in the constructor. Those lines are evaluated after the call to super (the parent constructor). In other words, what the JavaScript interpreter will really do is this :

class BaseClass {
    constructor(data) {
        this.baseField1 = undefined;
        this.baseField2 = undefined
        console.log(this.constructor.name + " has fields: " + Object.keys(this));
    }
}

class ChildClass extends BaseClass {
    constructor(data) {
        BaseClass.call(this, data);
        this.childField1 = undefined // too late !
    }
}

If you call Object.keys in the child constructor after super(), or anywhere that's not in a constructor, it will work as expected.

However, if your goal is specifically to log all the properties of a child instance immediately when it is constructed, and have this behaviour inherited from the parent class… I don't think you can, even with Proxies.

答案2

得分: -1

constructor(data: any) -- 这不是面向对象编程(OOP),这是伪装成的不良JavaScript习惯。

尽量具体化你的类接口。让它在构造函数中明确要求它需要哪些值,而不仅仅是一些不透明的对象。

class BaseClass {
  public constructor(
    private baseField1: string,
    private baseField2: string
  ) {
    // 如果类有方法,使用Object.keys(this)不是一个好主意
    console.log(this.constructor.name + " 包含字段: " + Object.keys(this));
  }
}

class ChildClass extends BaseClass {
  public constructor(
    baseField1: string,
    baseField2: string,
    private childField1: string
  ) {
    super(baseField1, baseField2);
  }
}

// 编译器不允许你只传递一个参数给构造函数
// 它确保对象的完整性
let base = new ChildClass('Joe');
英文:

constructor(data: any) -- this is not OOP, this is bad JavaScript habits under disguise.

Be as specific as possible with the interface of your class. Let it ask exactly what values it needs in the constructor, not just some opaque object.

class BaseClass {
  public constructor(
    private baseField1: string,
    private baseField2: string,
  ) {
    // Object.keys(this) is not a good idea if the class has methods
    console.log(this.constructor.name + " has fields: " + Object.keys(this));
  }
}

class ChildClass extends BaseClass {
  public constructor(
    baseField1: string,
    baseField2: string,
    private childField1: string;
  ){
    super(baseField1, baseField2);
  }
}

// The compiler does not allow you to pass only one argument to the constructor
// It ensures the object integrity
let base = new ChildClass('Joe');

huangapple
  • 本文由 发表于 2023年2月6日 21:45:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/75362119.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定