Issue 1:
Bug Report

Current Behavior

I have this legacy parameter decorator

function logProperty(target: any, property: string) {
var _val = this[property];

var getter = function () {
console.log(`Get: ${property} => ${_val}`);
return _val;
};

var setter = function (newVal) {
console.log(`Set: ${property} => ${newVal}`);
_val = newVal;
};

if (delete this[property]) {
Object.defineProperty(target, property, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}
}

depending on how I initialise a decorated property inside a class, it can be defined or undefined:

// Case 1
class Foo {
@logProperty public myProp: string = 'foo';
}

console.log((new Foo()).myProp)); // => undefined

// Case 2
class Foo {
@logProperty public myProp: string;

constructor() {
this.myProp = 'foo';
}
}

console.log((new Foo()).myProp)); // => 'foo'

Expected behavior/code

The console.log outputs of both test cases should be defined. In my case both should be 'foo'.

Babel Configuration (.babelrc, package.json, cli command)

{
"presets": [
"@babel/env",
"@babel/preset-typescript"
],
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose" : true }]
]
}

Environment

Babel version: 7.1.2

Node/npm version: Node 10.12.0/npm 6.4.1

OS: OSX 10.14

Monorepo no

How you are using Babel: tried with cli and loader

Possible Solution

I have no solution, but here are the compiled version of both test cases.

One has a class property, the other one defines the initial value in the initializer. Maybe that causes the issue.

Compiled code: Testcase 1

"use strict";

var _class, _descriptor;

function _initializerDefineProperty(target, property, descriptor, context) { if (!descriptor) return; Object.defineProperty(target, property, { enumerable: descriptor.enumerable, configurable: descriptor.configurable, writable: descriptor.writable, value: descriptor.initializer ? descriptor.initializer.call(context) : void 0 }); }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; Object['ke' + 'ys'](descriptor).forEach(function (key) { desc[key] = descriptor[key]; }); desc.enumerable = !!desc.enumerable; desc.configurable = !!desc.configurable; if ('value' in desc || desc.initializer) { desc.writable = true; } desc = decorators.slice().reverse().reduce(function (desc, decorator) { return decorator(target, property, desc) || desc; }, desc); if (context && desc.initializer !== void 0) { desc.value = desc.initializer ? desc.initializer.call(context) : void 0; desc.initializer = undefined; } if (desc.initializer === void 0) { Object['define' + 'Property'](target, property, desc); desc = null; } return desc; }

function _initializerWarningHelper(descriptor, context) { throw new Error('Decorating class property failed. Please ensure that ' + 'proposal-class-properties is enabled and set to use loose mode. ' + 'To use proposal-class-properties in spec mode with decorators, wait for ' + 'the next major version of decorators in stage 2.'); }

function logProperty(target, property) {
var _val = this[property];

var getter = function getter() {
console.log("Get: ".concat(property, " => ").concat(_val));
return _val;
};

var setter = function setter(newVal) {
console.log("Set: ".concat(property, " => ").concat(newVal));
_val = newVal;
};

if (delete this[property]) {
Object.defineProperty(target, property, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}
}

var Foo = (_class = function Foo() {
_classCallCheck(this, Foo);

_initializerDefineProperty(this, "myProp", _descriptor, this);
}, (_descriptor = _applyDecoratedDescriptor(_class.prototype, "myProp", [logProperty], {
configurable: true,
enumerable: true,
writable: true,
initializer: function initializer() {
return 'foo';
}
})), _class);
console.log(new Foo().myProp);

Compiled code: Testcase 2

"use strict";

var _class, _descriptor;

function _initializerDefineProperty(target, property, descriptor, context) { if (!descriptor) return; Object.defineProperty(target, property, { enumerable: descriptor.enumerable, configurable: descriptor.configurable, writable: descriptor.writable, value: descriptor.initializer ? descriptor.initializer.call(context) : void 0 }); }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; Object['ke' + 'ys'](descriptor).forEach(function (key) { desc[key] = descriptor[key]; }); desc.enumerable = !!desc.enumerable; desc.configurable = !!desc.configurable; if ('value' in desc || desc.initializer) { desc.writable = true; } desc = decorators.slice().reverse().reduce(function (desc, decorator) { return decorator(target, property, desc) || desc; }, desc); if (context && desc.initializer !== void 0) { desc.value = desc.initializer ? desc.initializer.call(context) : void 0; desc.initializer = undefined; } if (desc.initializer === void 0) { Object['define' + 'Property'](target, property, desc); desc = null; } return desc; }

function _initializerWarningHelper(descriptor, context) { throw new Error('Decorating class property failed. Please ensure that ' + 'proposal-class-properties is enabled and set to use loose mode. ' + 'To use proposal-class-properties in spec mode with decorators, wait for ' + 'the next major version of decorators in stage 2.'); }

function logProperty(target, property) {
var _val = this[property];

var getter = function getter() {
console.log("Get: ".concat(property, " => ").concat(_val));
return _val;
};

var setter = function setter(newVal) {
console.log("Set: ".concat(property, " => ").concat(newVal));
_val = newVal;
};

if (delete this[property]) {
Object.defineProperty(target, property, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}
}

var Foo = (_class = function Foo() {
_classCallCheck(this, Foo);

_initializerDefineProperty(this, "myProp", _descriptor, this);

this.myProp = 'foo';
}, (_descriptor = _applyDecoratedDescriptor(_class.prototype, "myProp", [logProperty], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
})), _class);
console.log(new Foo().myProp);

Issue 2:
Bug Report

Current Behavior

Babel 7 emits code with broken property injection.

Input Code

Reproducible repo

const LogUpdate = () => {
return (target: Object, key: string | symbol) => {
let val = target[key];

const getter = () => {
return val;
};

const setter = next => {
console.log(`Updating: ${val} => ${next}...`);
val = next;
};

Object.defineProperty(target, key, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
};
};

class Something {
@LogUpdate()
public count: number = 0;
}

const something = new Something();
something.count++;
something.count = 123;
something.count = -1;

Expected behavior/code

Code compiled with babel doesn't trigger logger:

elderapo@******:~/nodejs/babel-7-property-decorator-issue$ yarn babel-start
yarn run v1.13.0
$ yarn babel-build && node ./compiled-with-babel/index.js
$ babel src --out-dir compiled-with-babel --extensions ".ts,.tsx" --source-maps inline
Successfully compiled 1 file with Babel.
Done in 0.70s.

On the other hand code compipled with tsc works just fine:

elderapo@******:~/nodejs/babel-7-property-decorator-issue$ yarn ts-node-start
yarn run v1.13.0
$ ts-node ./src/index.ts
Updating: undefined => 0...
Updating: 0 => 1...
Updating: 1 => 123...
Updating: 123 => -1...
Done in 1.17s.

Babel Configuration (.babelrc, package.json, cli command)

{
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }]
],
"presets": ["@babel/env", "@babel/preset-typescript"]
}

Environment

Babel version(s): 7.4.0

Node/npm version: doesn't matter

OS: doesn't matter

Monorepo: doesn't matter

How you are using Babel: doesn't matter

Possible Solution

Additional context/Screenshots

This example is pretty trivial but property decorators are often used in dependency injection. It's possible to use https://github.com/inversify/InversifyJS to get stuff injected into constructor but it's not always possible. One of the examples is react ecosystem where it's really hard to use constructor injection and property injection comes in handy.