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

Use case: class/function registration #2

Closed
justinfagnani opened this issue Jun 10, 2016 · 6 comments
Closed

Use case: class/function registration #2

justinfagnani opened this issue Jun 10, 2016 · 6 comments

Comments

@justinfagnani
Copy link

Registering classes/functions could often invoke a registration function at class definition time, but not modify the object, such as with web components:

function define(tagname) {
  return (clazz) => {
    customElements.define(tagname, clazz);
    return clazz;
  };
}

@define('my-element')
class MyElement extends HTMLElement {}
@bicknellr
Copy link

Given that classes are expressions, why would something like this not be sufficient?

function define(tagname, clazz) => {
  customElements.define(tagname, clazz);
  return clazz;
}

define('my-element', class MyElement extends HTMLElement {})

?

Or, in this specific case:

customElements.define('my-element', class MyElement extends HTMLElement {})

@justinfagnani
Copy link
Author

The decorator case still defines a symbol for the class. Maybe more clear with an export:

@define('my-element')
export class MyElement extends HTMLElement {}

Decorators this simple might seem too trivial, but they are possible to statically analyze, and arguably still have ergonomic benefit.

@bicknellr
Copy link

bicknellr commented Jun 10, 2016

(EDIT: This is unrelated to your comment directly above, I hadn't checked to see if anything new came in before posting.)

I was about to reply that this would potentially suffer from some lispy )))))))-ness after the class, you can avoid this in the same way as mixin thing you mentioned a while ago:

function decoratorA(Class) {...}
function decoratorB(Class) {...}
function decoratorC(Class) {...}

decoratorA(decoratorB(decoratorC(class MyElement extends HTMLElement {
  ...
}))) // <- line noise; tedious to match

function decorate(...args) {
  let Class = args.pop();
  while (args.length) {
    Class = args.pop()(Class);
  }
  return Class;
}

decorate(decoratorA, decoratorB, decoratorC,
class MyElement extends HTMLElement {
  ...
}) // <- always one (

@bicknellr
Copy link

To export the symbol:

export const MyElement =
decorate(decoratorA, decoratorB, decoratorC,
class extends HTMLElement {
  ...
});

But, yes, this would imply that there's no canonical class decorator behavior and any static analysis tool would have to know about the specific implementation of decorators you're using.

Also, I don't think this can provide a generic static analysis tool any useful information: either the decorator's implementation must be known by that tool ahead of time - at which point that specific decorator has become part of the language itself - or the implementation isn't known and the analysis wouldn't be assisted by changing the syntax from wrapping with a function to decorating with a function. For example, if you were attempting to perform some kind of type checking, decorating with a function won't provide any special implication that the type signatures of the methods of the decorated class won't change if the decorator has the opportunity to arbitrarily change those methods.

@justinfagnani
Copy link
Author

Yes, a static analysis tool would have to know about the decorator, but decorator syntax is much more amenable to analysis. So the Polymer Analyzer, which looks for element declarations, need only look for class declarations decorated with @define, rather than try to look for calls to customElements.define, which may be called from another function.

Your example of composing decorator functions would fool most static analysis. In my experience with very similar metadata annotations in other languages, @foo is more likely to be directly used in a manner that can be detected.

@littledan
Copy link
Member

I'm doing a scrub of these bugs. Happy to say that this is still supported in the current version! You can include this registration in a class finisher function.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants