tc39 / proposal-decorators

Decorators for ES6 classes
https://arai-a.github.io/ecma262-compare/?pr=2417
2.76k stars 106 forks source link

discrepancy: we're allowed to get/set private fields, but not public fields, and public auto accessors are not fields. #550

Open trusktr opened 1 month ago

trusktr commented 1 month ago

[!Note] This post comes after experience using decorators with TypeScript and Babel, including the new Stage 3 decorators since day one, for years.

It seems to me that the argument for performance in not allowing class fields to be accessed with (or converted to) get/set pairs is moot, because accessor #foo = 123 is exactly equivalent to the very same idea, and yet we allow it.

The problem

In this code,

class Foo {
  accessor #foo = 123 //  this creates a `get`/`set` pair *somewhere*!

  accessor foo = 123
}

The #foo and foo properties are not the same at all. The private field is associated with the instance, while the public field is defined on the prototype.

This is a big discrepancy.

First and foremost, converting foo = 123 to accessor foo = 123 can, and will, break certain code bases.

The half-way defined accessor feature (when decorators are released, the feature will be half-way implemented, with the other half in a separate spec) does not even need to exist in this round of decorators, because if we simply fix the timing of context.addInitializer() for getter/setter elements, as described in

then, for the most part, important use cases that include reading initial values from getters/setters are covered. Please show a case where you absolutely need the getter/setter extra initializer to run before fields.

Don't get me wrong, accessor syntax is concise and more enjoyable than getters/setters, but not fundamentally enabling anything new, while decorators provide new confusing differences between auto accessor and regular accessors as in #548.

Solution

It would be more desirable, more consistent, and more breakage avoiding, to be able to provide the ability to provide get and set functions for class fields, with the same or similar semantics as private fields which we've already blessed.

Two ways to do this:

Regardless of which of the previous two are picked, then if we also:

then at this point class fields would be as useful as accessor including with initial values, which would effectively be eliminate the need for accessor in this iteration of decorators.

Accessors would still be nice sugar for prototype getters/setters, but basically unnecessary for shipping decorators.

pzuraq commented 1 month ago

@trusktr so I've been trying to tell you a few things for a while now, gunna lay it out one more time.

  1. I know the performance concerns don't seem to make sense to you from time to time in this design. They also did not make sense to me. A lot of this I believe came from the uncertainty of dynamic class features and behaviors that engines only realized once they had started implement class fields with [[Define]] semantics and saw that they were huge performance regressions initially. So I think they basically adopted a "let's play it extremely safe" stance, which meant that they either did not want decorators at all, or would only accept a version where as much behavior as possible was statically knowable at parse time.
  2. The changes you keep suggesting at this point in the process would require that the stage 3 spec be moved back to stage 2. At that point, it could once again be challenged by any TC39 member and potentially completely halted in progress. It could be killed, because stage 2 is a lot less certain than stage 3.

I have and the other champions have no control over 1, and we are not willing to risk the consequences of 2. I understand your frustrations, but this is the best spec we could have had given the constraints and the fact that there were a number of members who were almost ready to kill the proposal entirely. It was frankly a miracle that we got consensus to have the features and capabilities we have.