skip to Main Content

I’d like to be able to instantiate an object with some props, and then later update that object with the same props, and run them through the same logic.

class A {
  #a = ''
  constructor(props = {}) {
    this.update(props)
  }
  update(props) {
    this.#a = props.a ?? ''
  }
}
a = new A({a: '123'})

I’m using private properties here, which I didn’t think was a big deal, until I made a subclass:

class B extends A {
  #b = ''
  update(props) {
    super.update(props)
    this.#b = props.b ?? ''
  }
}
b = new B({a: 'a', b: 'b'})
# TypeError: Cannot write private member #b to an object whose class did not declare it

Avoiding private properties solves it, but that’s just avoiding the problem, not fixing it.

Any ideas?

2

Answers


  1. The issue is that while A’s constructor is being called, you can’t use B’s private fields – they’re created between the construction of the A part of the instance and the B part, essentially.

    One option is to only apply your own updates in the constructor, and have the superconstructor take care of the rest. (This also avoids construction time scaling with the product of the inheritance depth and the number of properties, although one hopes that wouldn’t become significant.) Not great, but I’m not sure if you can have all the nice parts at once.

    class A {
      #a = ''
      constructor(props = {}) {
        this.#update(props)
      }
      #update(props) {
        this.#a = props.a ?? ''
      }
      update(props) {
        this.#update(props)
      }
    }
    let a = new A({a: '123'})
    
    class B extends A {
      #b = ''
      constructor(props = {}) {
        super(props)
        this.#update(props)
      }
      #update(props) {
        this.#b = props.b ?? ''
      }
      update(props) {
        super.update(props)
        this.#update(props)
      }
    }
    let b = new B({a: 'a', b: 'b'})

    Or maybe this is better, saving some duplication at the cost of making constructors more complicated and requiring the private field defaults to be correct or callers to never forget to pass props:

    class A {
      #a = ''
      constructor(props) {
        if (props != null) this.update(props)
      }
      update(props) {
        this.#a = props.a ?? ''
      }
    }
    let a = new A({a: '123'})
    
    class B extends A {
      #b = ''
      constructor(props) {
        super(null)
        if (props != null) this.update(props)
      }
      update(props) {
        super.update(props)
        this.#b = props.b ?? ''
      }
    }
    let b = new B({a: 'a', b: 'b'})
    Login or Signup to reply.
  2. A solution that doesn’t require any changes in A class:

    class A {
      #a = ''
      constructor(props = {}) {
        this.update(props)
      }
      update(props) {
        this.#a = props.a ?? ''
      }
    }
    a = new A({a: '123'});
    
    class B extends A {
      #b = ''
      
      // create a constructor for B and init 'this' with super() without arguments
      // then call update() manually with proper this (B instance)
      
      constructor(props){
        super();
        this.update(props);
      }
      update(props) {
        super.update(props);
        
        // allow this work in the A's constructor without arguments
        'b' in props && ( this.#b = props.b ?? '' );
      }
      dump(){
        console.log('#b:', this.#b);
      }
    }
    
    b = new B({a: 'a', b: 'b'});
    b.dump();
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search