←back to thread

277 points merqurio | 2 comments | | HN request time: 0.43s | source
Show context
epolanski ◴[] No.45113064[source]
Great project but I can't stand syntax such as decorators.
replies(5): >>45113194 #>>45113315 #>>45113317 #>>45113613 #>>45114400 #
jfagnani ◴[] No.45113317[source]
Decorators are the only way to metaprogram over class fields in JS. Otherwise they're not even detectable on the prototype.

We use them to make fields reactive mostly, and I love how declarative they are. But we use them sparingly. I personally don't love how some libraries try to put a lot of things into decorators that could have been standard class features, like a static field or a method.

edit: As mentioned by skrebbel, decorators are optional. Every decorator has a simple plain-JS way of doing it. Event reactive properties: https://lit.dev/docs/components/properties/#declaring-proper...

We also put a lot of effort into making all of our documentation and playground samples on lit.dev available in both JavaScript and TypeScript with decorators. There's a switch that will change everything on the site from JS to TS globally.

replies(2): >>45113690 #>>45114413 #
1. epolanski ◴[] No.45113690[source]
> Decorators are the only way to metaprogram over class fields in JS.

I can think about few other ways, such as using higher order functions/classes, using getOwnPropertyDescriptor or doing stuff at construction.

> As mentioned by skrebbel, decorators are optional

This is not a pro, it's a con. The more ways there are to achieve the same result the more inconsistent projects become IRL.

Also, do you really want to metaprogram at all? What's the huge benefit of that approach?

replies(1): >>45113856 #
2. jfagnani ◴[] No.45113856[source]
There really is no way to metaprogram against class fields except with decorators.

Class fields aren't on the prototype. They're essentially Object.defineProperty() calls that occur right after the super() call of a constructor. You can't get at them at all from the class declaration.

I know more than one way to do something is sometimes a downside, but we have a large audience an a huge portion of it wants to use TypeScript and declarators, and another huge portion of it wants to use vanilla JavaScript. We factored things to give the best DX we could to each group with relatively little cost to us.

As for why metaprogramming, we want a component to update when it's state is changed. Like plain web components, Lit uses classes.

So here, we want setting `el.name` to cause the component to re-render:

    class MyElement extends LitElement {

      name = 'World';

      render() {
        return html`<h1>Hello ${this.name}</h1>`
      }
    }
We do that by replacing `name` with a getter/setter pair where the setter schedules an update of the instance. Without decorators, there's no way to see that `name` even exists, much less replace it.

Decorators actually do the replacement for us, we just have to build the thing to replace it with:

    class MyElement extends LitElement {

      @property() accessor name = 'World';

      render() {
        return html`<h1>Hello ${this.name}</h1>`
      }
    }