tc39/proposal-decorators

Is `super` available in `accessor` initialization?

Closed this issue · 9 comments

Would this code work?

class A {
  accessor a = [1];
}

class B {
  accessor a = [...super.a, 2];
}

console.log(new B().a) // [1,2] ?

Class B need a extends to A:

class A {
  accessor a = [1];
}

class B extends A {
  accessor a = [...super.a, 2];
}

console.log(new B().a) // [1,2] ?

Thanks @pabloalmunia . Forgot that. Now it works correctly with your transpiler (But still looks like the proposal's specification doesn't mention super availability with accessor).

The right part of the property assignment can be complex. Anything valid for initializing a property must be valid for initializing a property defined with the accessor keyword.

Correct, this is building on the class fields proposal, and the initializer would be the same as the one defined for class fields. In class fields, you can access super, so you would also be able to in auto-accessors.

By examples without transpilation, as plain code:

This code is wrong:

class A {
  a = [1];
}

class B extends A {
  a = [...super.a, 2];   // TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator))
}

console.log(new B().a)

But, this other code is valid:

class A {
  #a = [1];
  get a () {
    return this.#a;
  }
  set a(v) {
    this.#a = v;
  }
}

class B extends A {
  constructor () {
    super();
  }
  a = [...super.a, 2];
}
console.log(new B().a) // [1,2] Ok

And this other code is valid (without constructor):

class A {
  #a = [1];
  get a () {
    return this.#a;
  }
  set a(v) {
    this.#a = v;
  }
}

class B extends A {
  a = [...super.a, 2];
}
console.log(new B().a) // [1,2] Ok

In my opinion, as a result, this other code is valid too:

class A {
  accessor a = [1];
}

class B extends A {
  a = [...super.a, 2];
}

console.log(new B().a) // [1,2] Ok
class A {
  a = [1];
}

class B extends A {
  a = [...super.a, 2];   // TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator))
}

console.log(new B().a)

That's what I was curious about. In current implementation of class fields in browsers, super can't be used. And accessor can provide this functionality.

In my opinion, as a result, this other code is valid too:

I agree with this.

Well, it's not that super can't be used exactly, it's that the class field does not exist on the value that super is pointing to. Class fields are defined on the instance, not on the super class. If you reference a method instead, it would work.

class A {
  getA() {
    return [1];
  }
}

class B extends A {
  a = [...super.getA(), 2]; 
}

console.log(new B().a); // [1, 2]

Likewise, if you reference an accessor, and if that accessor references a field, it will also work:

class A {
  #a = [1];

  get a() {
    return this.#a;
  }
}

class B extends A {
  a = [...super.a, 2]; 
}

console.log(new B().a)

Because the class fields of the super class are initialized before the fields of the subclass, and because get a() on the super class is referencing the instance with this.

Auto-accessors would also work this way, so you could in theory reference them with super if they were defined on the parent with accessor, but the opposite would not work:

class A {
  accessor a = [1];
  b = [1];
}

class B extends A {
  // this works
  a = [...super.a, 2]; 

  // this does not
  accessor b = [...super.b, 2];
}

console.log(new B().a)

Edit: Noticed @pabloalmunia basically responded the same way 😅 hopefully this clarifies everything further!

Thanks @pabloalmunia & @pzuraq . This cleared up the curiosity to me. Closing the issue.