Skip to content
This repository has been archived by the owner on Jan 25, 2022. It is now read-only.
This repository has been archived by the owner on Jan 25, 2022. It is now read-only.

Field declarations overwrite properties on the prototype #151

Closed
@mweststrate

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions