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 in MobX, when decorator syntax is not used. (See decorate
docs)
class Counter {
counter = 0
}
decorate(Counter, { counter: observable })
Which is currently semantically equivalent to:
class Counter {
@observable counter = 0
}
This first example works fine with TypeScript and babel-plugin-proposal-class-properties, but only if loose
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, the decorate
call introduces the count
property on the Counter
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 code decorate
does something along these lines:
class Counter {
counter = 3
}
Object.defineProperty(Counter.prototype, "counter", {
set(counter) {
this._counter = counter * 2
},
get() {
return this._counter * 2
}
})
const counter = new Counter()
console.log(counter.counter)
Printing the counter
will yield 12
in loose mode, but 3
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 the decorate
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:
var Counter = function Counter() {
this.x = 3 // assignment being picked up by property on the prototype
}
// Standard:
var Counter = function Counter() {
Object.defineProperty(obj, "counter", { // Property definition hides prototype property
value: 3,
enumerable: true,
configurable: true,
writable: true
});
};
Activity