Just for the record: while writing this I came to a conclusion that "all" of my problems would be solved if I only have used the decorators in the Python way, i. e. only for classes and methods, and not for properties. I'm still leaving my thoughts though, as maybe they'll prove useful in one way or the other.
I have a problem with the temporary initializer descriptor property.
I wanted to decorate a property of a plain object and found out that babel is doing something which seemed strange to me. The value descriptor property was nowhere to be found when my decorator was called, and instead I've found an initializer property on the descriptor. That led me to this spec and the spec about class fields. After finding no clues as to why should that property even exist when I was dealing with a plain object, I've first created an issue in the babel repo, and then turned here.
I'll now present my thoughts on the matter of the issue. Please be gentle with me.
First, let's look at a simple object.
let person = {
born: Date.now()
};
If we'd really like to use the Object.defineProperty function to highlight what's going on with the descriptor, it would look like this:
let person = {};
Object.defineProperty(person, 'born', {
value: Date.now(),
enumerable: true,
writable: true,
configurable: true,
};
I don't really see a reason to introduce another descriptor property here.
Now, the way a decorator would handle this if we didn't know classes existed is quite straightforward - just do the same thing as with the methods. But, enter classes - and boom, everything has to be changed. I know that in reality we have classes, but I wanted to take you to a different world for a second to provide another perspective.
Let's take a look at the classes now.
I've read the es-class-static-properties-and-fields spec and didn't find any clues to the initializer property. That's why I was puzzled by the example in INITIALIZER_INTEROP.md from this repo:
class Person {
born = Date.now();
}
// desugars to roughly:
class Person {
constructor() {
let _born = Object.getPropertyDescriptor(this, 'born');
this.born = _born.initializer.call(this);
}
}
Object.defineProperty(Person.prototype, 'born', {
initializer() { return Date.now() },
enumerable: true,
writable: true,
configurable: true,
});
I thought that doing it this way didn't match what was described in the class fields spec. So I've decided to try and do it in another way:
class Person {
constructor() {
Object.defineProperty(this, 'born', {
value: initializer.call(this),
enumerable: true,
writable: true,
configurable: true,
});
}
}
let initializer = function () {
return Date.now();
};
This solution doesn't clutter the prototype and otherwise behaves like the original solution - has the proper scoping and the initializer is called properly. Then I tried adding a decorator to my solution:
class Person {
@readonly
born = Date.now();
}
// is desugared to
class Person {
constructor() {
let desc = {
value: initializer.call(this),
enumerable: true,
writable: true,
configurable: true,
};
desc = readonly(this, 'born', desc) || desc;
Object.defineProperty(this, 'born', desc);
}
}
let initializer = function () {
return Date.now();
};
Things here differ from the original solution a bit more than in the previous situation:
- the decorator is called with
this as a target and not with a prototype - that seems more correct to me, as the property is being assigned to the instantiated object and not the prototype, so this seems more proper as a "target"
- the decorator is applied each time the constructor is ran, instead of applying it only once. But then the field that is decorated isn't "applied" only once - its expression is evaluated each time an object is instantiated, so the decorator behavior is more in sync with what's happening with the field
I think that decorators should operate on the standard descriptor because it's possible, as I've demonstrated above.
Those are my honest thoughts. If there was already a similar discussion about these problems, please point me there.
Just for the record: while writing this I came to a conclusion that "all" of my problems would be solved if I only have used the decorators in the Python way, i. e. only for classes and methods, and not for properties. I'm still leaving my thoughts though, as maybe they'll prove useful in one way or the other.
I have a problem with the temporary
initializerdescriptor property.I wanted to decorate a property of a plain object and found out that babel is doing something which seemed strange to me. The
valuedescriptor property was nowhere to be found when my decorator was called, and instead I've found aninitializerproperty on the descriptor. That led me to this spec and the spec about class fields. After finding no clues as to why should that property even exist when I was dealing with a plain object, I've first created an issue in the babel repo, and then turned here.I'll now present my thoughts on the matter of the issue. Please be gentle with me.
First, let's look at a simple object.
If we'd really like to use the
Object.definePropertyfunction to highlight what's going on with the descriptor, it would look like this:I don't really see a reason to introduce another descriptor property here.
Now, the way a decorator would handle this if we didn't know classes existed is quite straightforward - just do the same thing as with the methods. But, enter classes - and boom, everything has to be changed. I know that in reality we have classes, but I wanted to take you to a different world for a second to provide another perspective.
Let's take a look at the classes now.
I've read the es-class-static-properties-and-fields spec and didn't find any clues to the
initializerproperty. That's why I was puzzled by the example in INITIALIZER_INTEROP.md from this repo:I thought that doing it this way didn't match what was described in the class fields spec. So I've decided to try and do it in another way:
This solution doesn't clutter the prototype and otherwise behaves like the original solution - has the proper scoping and the initializer is called properly. Then I tried adding a decorator to my solution:
Things here differ from the original solution a bit more than in the previous situation:
thisas a target and not with a prototype - that seems more correct to me, as the property is being assigned to the instantiated object and not the prototype, sothisseems more proper as a "target"I think that decorators should operate on the standard descriptor because it's possible, as I've demonstrated above.
Those are my honest thoughts. If there was already a similar discussion about these problems, please point me there.