- Notifications
You must be signed in to change notification settings - Fork113
Field declarations overwrite properties on the prototype #151
Description
I think this issue has been raised a few times already in several of the open issues, like#100, but I thought it would probably be good to report a very specific, real life use case.
The following snippet is the official way to enhance a class with observable capabalities inMobX, when decorator syntax is not used. (Seedecorate
docs)
classCounter{counter=0}decorate(Counter,{counter:observable})
Which is currently semantically equivalent to:
classCounter{ @observablecounter=0}
This first example works fine with TypeScript andbabel-plugin-proposal-class-properties, but only ifloose
mode is used (that translates the initializes to an assignment in the constructor). In standards mode, this mechanism no longer works.
The simple reason is, thedecorate
call introduces thecount
property on theCounter
property, and gives it a getter and setter. If just an assignment is made in the constructor, this works fine. However, if a new property is introduces, the property on the prototype is completely ignored and a new one is introduced on the instance.
In pseudo codedecorate
does something along these lines:
classCounter{counter=3}Object.defineProperty(Counter.prototype,"counter",{set(counter){this._counter=counter*2},get(){returnthis._counter*2}})constcounter=newCounter()console.log(counter.counter)
Printing thecounter
will yield12
in loose mode, but3
in standards mode, as in standards mode the field would be re-introduced in a non-interceptable way.
I search this repo for it, but couldn't really find it, what was the motivation to change the spec in this regards? (or if it never changed; what is the motivation to deviate from what existing transpilers are doing?).
And more specific: how could I express "decorating" my class fields in the future with the above API? After all, changing a prototype after the initial class declaration is not that uncommon in JavaScript.
A potentially future risk of this approach is that it will render MobX completely incompatible with create-react-app, if this proposal is finalized before the decorators standard, as thedecorate
utility is currently the only way in which CRA users can obtain decorator-like behavior without the syntax. For some background:https://mobx.js.org/best/decorators.html
N.B. note that this problem also happens when the field is declared, but never initialized.
Edit: for clarification to other readers of this issue, the difference in compilation between loose and standards mode is:
// Loose:varCounter=functionCounter(){this.x=3// assignment being picked up by property on the prototype}// Standard:varCounter=functionCounter(){Object.defineProperty(obj,"counter",{// Property definition hides prototype propertyvalue:3,enumerable:true,configurable:true,writable:true});};