{"action":"created","issue":{"url":"https://api.github.com/repos/tc39/proposal-class-fields/issues/151","repository_url":"https://api.github.com/repos/tc39/proposal-class-fields","labels_url":"https://api.github.com/repos/tc39/proposal-class-fields/issues/151/labels{/name}","comments_url":"https://api.github.com/repos/tc39/proposal-class-fields/issues/151/comments","events_url":"https://api.github.com/repos/tc39/proposal-class-fields/issues/151/events","html_url":"https://github.com/tc39/proposal-class-fields/issues/151","id":371233558,"node_id":"MDU6SXNzdWUzNzEyMzM1NTg=","number":151,"title":"Field declarations overwrite properties on the prototype","user":{"login":"mweststrate","id":1820292,"node_id":"MDQ6VXNlcjE4MjAyOTI=","avatar_url":"https://avatars0.githubusercontent.com/u/1820292?v=4","gravatar_id":"","url":"https://api.github.com/users/mweststrate","html_url":"https://github.com/mweststrate","followers_url":"https://api.github.com/users/mweststrate/followers","following_url":"https://api.github.com/users/mweststrate/following{/other_user}","gists_url":"https://api.github.com/users/mweststrate/gists{/gist_id}","starred_url":"https://api.github.com/users/mweststrate/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mweststrate/subscriptions","organizations_url":"https://api.github.com/users/mweststrate/orgs","repos_url":"https://api.github.com/users/mweststrate/repos","events_url":"https://api.github.com/users/mweststrate/events{/privacy}","received_events_url":"https://api.github.com/users/mweststrate/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":158,"created_at":"2018-10-17T19:31:16Z","updated_at":"2018-11-21T08:46:01Z","closed_at":null,"author_association":"NONE","body":"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. \r\n\r\nThe following snippet is the official way to enhance a class with observable capabalities in [MobX](https://github.com/mobxjs/mobx), when decorator syntax is not used. (See [`decorate` docs](https://mobx.js.org/refguide/api.html#-decorate))\r\n\r\n```javascript\r\nclass Counter {\r\n  counter = 0\r\n}\r\n\r\ndecorate(Counter, { counter: observable })\r\n```\r\n\r\nWhich is currently semantically equivalent to:\r\n\r\n```javascript\r\nclass Counter {\r\n  @observable counter = 0\r\n}\r\n```\r\n\r\nThis first example works fine with TypeScript and [babel-plugin-proposal-class-properties](https://babeljs.io/docs/en/next/babel-plugin-proposal-class-properties.html), 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. \r\n\r\nThe 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. \r\n\r\nIn pseudo code `decorate` does something along these lines: \r\n\r\n```javascript\r\nclass Counter {\r\n    counter = 3\r\n}\r\n\r\nObject.defineProperty(Counter.prototype, \"counter\", {\r\n  set(counter) {\r\n    this._counter = counter * 2 \r\n  },\r\n  get() {\r\n    return this._counter * 2 \r\n  }\r\n})\r\n\r\nconst counter = new Counter()\r\n\r\nconsole.log(counter.counter)\r\n```\r\n\r\nPrinting 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.\r\n\r\n---\r\n\r\nI 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?).\r\n\r\nAnd 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. \r\n\r\n---\r\n\r\nA 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\r\n\r\nN.B. note that this problem also happens when the field is declared, but never initialized.\r\n\r\n---\r\n\r\n_Edit: for clarification to other readers of this issue, the difference in compilation between loose and standards mode is:_\r\n\r\n```javascript\r\n// Loose:\r\nvar Counter = function Counter() {\r\n   this.x = 3 // assignment being picked up by property on the prototype\r\n}\r\n\r\n// Standard:\r\nvar Counter = function Counter() {\r\n   Object.defineProperty(obj, \"counter\", { // Property definition hides prototype property\r\n      value: 3,\r\n      enumerable: true,\r\n      configurable: true,\r\n      writable: true\r\n    });\r\n};\r\n```\r\n"},"comment":{"url":"https://api.github.com/repos/tc39/proposal-class-fields/issues/comments/440582956","html_url":"https://github.com/tc39/proposal-class-fields/issues/151#issuecomment-440582956","issue_url":"https://api.github.com/repos/tc39/proposal-class-fields/issues/151","id":440582956,"node_id":"MDEyOklzc3VlQ29tbWVudDQ0MDU4Mjk1Ng==","user":{"login":"trotyl","id":6059170,"node_id":"MDQ6VXNlcjYwNTkxNzA=","avatar_url":"https://avatars0.githubusercontent.com/u/6059170?v=4","gravatar_id":"","url":"https://api.github.com/users/trotyl","html_url":"https://github.com/trotyl","followers_url":"https://api.github.com/users/trotyl/followers","following_url":"https://api.github.com/users/trotyl/following{/other_user}","gists_url":"https://api.github.com/users/trotyl/gists{/gist_id}","starred_url":"https://api.github.com/users/trotyl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/trotyl/subscriptions","organizations_url":"https://api.github.com/users/trotyl/orgs","repos_url":"https://api.github.com/users/trotyl/repos","events_url":"https://api.github.com/users/trotyl/events{/privacy}","received_events_url":"https://api.github.com/users/trotyl/received_events","type":"User","site_admin":false},"created_at":"2018-11-21T08:46:01Z","updated_at":"2018-11-21T08:46:01Z","author_association":"NONE","body":"> One of the core features of OOP and inheritance is the notion that a \"base class\" both defines capabilities and sets constraints on anything that inherits it.\r\n\r\n@rdking Really, from my knowledge in OOP the derived class field can always override property in base class, consider the C# code (Java has no property):\r\n\r\n```csharp\r\nclass Base {\r\n  int Foo { get; }\r\n}\r\n\r\nclass Derived: Base {\r\n  public int Foo = 42;\r\n}\r\n```\r\n\r\nOnline demo: https://repl.it/repls/AuthorizedFragrantCron\r\n\r\nIt fully works in OOP, and the reason is that a field is always a declaration, not a mutation, thus it can never touches base class property, regardless of what constraint on the parent class.\r\n\r\nSo what's the reason for making it non-OOP (`[[Set]]`) and then introduce extra runtime-macro to make it alike?\r\n\r\n> it doesn't make much sense to put a property on an instance without giving it a default value.\r\n\r\nThat's why fields being a different concept than normal property, it always involves memory allocation, so the default value is not an assignment result, but the language semantic of uninitialized value."}}
{"actor":{"display_login":"trotyl"}}