Open littledan opened 6 years ago
I'd would prefer to leave it for a follow on.
Maybe this can be closed unless there's a clear benefit. We can avoid designing class decorators in such a way as the linked issue.
For example, instead of something like this (coughAngularcough):
@component({
props: {
foo: Number,
},
template: html`
<div>100 lines of HTML. {{foo}}</div>
`
})
class MyComponent extends Component {} // why does this empty class even exist?
design it in a more semantic way:
class MyComponent extends Component {
@numberAttribute accessor foo = 123
template = () => html`
<div>100 lines of HTML. ${this.foo}</div>
`
}
Here's a real-world example:
The obvious use case that comes to mind for this is metadata (tc39/proposal-decorator-metadata). You may have need to apply some substantive object of metadata, and doing so before the class declaration leads to the issues discussed in the previous thread: You end up burying the actual class under a bunch of junk.
Another aspect worth considering, is that sometimes library authors just make bad decisions, and as consumers of those libraries we are stuck dealing with them. This concept of inline decorators would allow for a language level escape, where even if a library relies heavily on decorators for things that could be class properties, we could still write our own code in a somewhat cleaner way.
If the example from above is existing Angular:
@component({
props: {
foo: Number,
},
template: html`
<div>100 lines of HTML. {{foo}}</div>
`
})
class MyComponent extends Component {} // why does this empty class even exist?
Are they really going to rework that? Or is everyone using Angular just going to be stuck with it forever.
Another consideration, is that inevitably there will come libraries that only contain decorators. Ones that allow you to declaratively enhance a class without relying on the class having any particular shape. For that use-case, they will rely entirely on what you can pass to the decorator and dictating the need for a particular class member to be defined wouldn't work.
All that being said, the proposed syntax from the other discussion is questionable in my opinion:
class MyComponent extends Component {
@decoratorA {
// maybe 1-100 lines
}
@decoratorB {
// another long thing (maybe some html template)
}
}
This assumes that the argument to the decorator function is an object, which isn't really appropriate.
I would suggest that the same general calling convention be maintained, with the one exception being that you need to end the decorator expression with a semicolon ;
to indicate that you are decorating the class (implicitly since no member follows the decorator) rather than the next member that is defined:
class MyComponent extends Component {
@decoratorA({
// maybe 1-100 lines
});
@decoratorB({
// another long thing (maybe some html template)
});
}
This would also help with applying multiple decorators to a single class, for instance if you had a big old stack of decorators like this:
/**
* This is my class, it does stuff
*/
@singleton
@logged("info")
@registered(uuid)
@someOtherDecorator("with", "multiple", "argument")
@thisOneHas({ anObject: "argument" })
@metadata({ /* Whatever */ })
class MyClass {
// ...
}
This is just as much of a big ugly mess, and what if each decorator in that stack comes from some different library? The all do something useful, but boy is that a log of extra stuff before you get to the actual name of the class!
Instead, it could be:
/**
* This is my class, it does stuff
*/
class MyClass {
@singleton;
@logged("info");
@registered(uuid);
@someOtherDecorator("with", "multiple", "argument");
@thisOneHas({ anObject: "argument" });
@metadata({ /* Whatever */ });
// ...
}
The biggest problem I see with this, is what order do you apply these?
/**
* This is my class, it does stuff
*/
@singleton
@logged("info")
@registered(uuid)
class MyClass {
@someOtherDecorator("with", "multiple", "argument");
@thisOneHas({ anObject: "argument" });
@metadata({ /* Whatever */ });
// ...
}
If you use both syntax options (you shouldn't, but someone will) what order are they applied? Usually they go in reverse order, with the last decorator receiving the class, and passing the decorated version up the chain. But for inline ones, does that order make sense? What if the decorators are down at the bottom of the class definition? Or some at the top, and some at the bottom? It could get really confusing what the order would be. And do the inline ones come before the preceding ones? Or after?
Honestly, a potentially more logical option might be to propose the ability to put decorators after a class definition, but I'm not sure what that would look like.
Previous discussion: https://github.com/tc39/proposal-decorators-previous/issues/22
OK to leave for v2?