Skip to content

Commit c716f90

Browse files
justinfagnaniAndrewJakubowiczaugustjk
authored
Add standard decorator documentation (#1218)
Co-authored-by: Andrew Jakubowicz <[email protected]> Co-authored-by: Augustine Kim <[email protected]>
1 parent 03ee5b7 commit c716f90

File tree

1 file changed

+101
-79
lines changed

1 file changed

+101
-79
lines changed

packages/lit-dev-content/site/docs/v3/components/decorators.md

Lines changed: 101 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -9,50 +9,37 @@ versionLinks:
99
v2: components/decorators/
1010
---
1111

12-
Decorators are special functions that can modify the behavior of classes, class methods, and class fields. Lit uses decorators to provide declarative APIs for things like registering elements, reactive properties, and queries.
12+
Decorators are functions that can be used to declaratively annotate and modify the behavior of classes.
1313

14-
Decorators are a [stage 3 proposal](https://github.com/tc39/proposal-decorators) for addition to the ECMAScript standard. Currently no browsers implement decorators, but compilers like [Babel](https://babeljs.io/) and [TypeScript](https://www.typescriptlang.org/) provide support for an earlier version of the decorators proposal. Lit decorators work with Babel and TypeScript, and will be updated to work with the final specification when it's implemented in browsers.
14+
Lit provides a set of optional decorators that enable declarative APIs for things like registering elements, defining reactive properties and query properties, or adding event options to event handler methods.
1515

16-
See the [Enabling decorators](#enabling-decorators) section for more information.
17-
18-
{% aside "info" %}
19-
20-
What does stage 3 mean?
21-
22-
Stage 3 means that the specification text is complete, and ready for browsers to implement. Once the specification has been implemented in multiple browsers, it can move to the final stage, stage 4, and be added to the ECMAScript standard. A stage 3 proposal can still change, but only if critical issues are discovered during implementation.
23-
24-
{% endaside %}
25-
26-
27-
28-
Lit supplies a set of decorators that reduce the amount of boilerplate code you need to write when defining a component. For example, the `@customElement` and `@property` decorators make a basic element definition more compact:
16+
For example, the `@customElement` and `@property()` decorators let you register a custom element and define a reactive property in a compact, declarative way:
2917

3018
```ts
3119
@customElement('my-element')
3220
export class MyElement extends LitElement {
33-
@property() greeting = "Welcome";
34-
@property() name = "Sally";
35-
@property({type: Boolean}) emphatic = true;
36-
//...
37-
}
38-
```
39-
{#custom-element}
4021

41-
The `@customElement` decorator defines a custom element, equivalent to calling:
22+
@property()
23+
greeting = 'Welcome';
4224

43-
```js
44-
customElements.define('my-element', MyElement);
25+
}
4526
```
4627

47-
The `@property` decorator declares a reactive property.
28+
{% aside "info" "no-header"%}
4829

49-
See [Reactive properties](/docs/v3/components/properties/) for more information about configuring properties.
30+
Lit supports two different versions of the JavaScript decorators proposal – an early version supported by TypeScript that we refer to as _experimental decorators_ and a new and final version we refer to as _standard decorators_.
31+
32+
There are some small differences in usage between the two proposals (standard decorators often require the `accessor` keyword). Our code samples are written for experimental decorators because we recommend them for production at the moment.
33+
34+
See [Decorator versions](#decorator-versions) for more details.
35+
36+
{% endaside %}
5037

5138
## Built-in decorators
5239

5340
| Decorator | Summary | More Info |
5441
|-----------|---------|--------------|
55-
| {% api-v3 "@customElement" "customElement" %} | Defines a custom element | [Above](#custom-element) |
42+
| {% api-v3 "@customElement" "customElement" %} | Defines a custom element. | [Defining](/docs/v3/components/defining/) |
5643
| {% api-v3 "@eventOptions" "eventOptions" %} | Adds event listener options. | [Events](/docs/v3/components/events/#event-options-decorator) |
5744
| {% api-v3 "@property" "property" %} | Defines a public property. | [Properties](/docs/v3/components/properties/#declare-with-decorators) |
5845
| {% api-v3 "@state" "state" %} | Defines a private state property | [Properties](/docs/v3/components/properties/#declare-with-decorators) |
@@ -64,7 +51,7 @@ See [Reactive properties](/docs/v3/components/properties/) for more information
6451

6552
## Importing decorators
6653

67-
You can import all the lit decorators via the `lit/decorators.js` module:
54+
You can import all of the Lit decorators via the `lit/decorators.js` module:
6855

6956
```js
7057
import {customElement, property, eventOptions, query} from 'lit/decorators.js';
@@ -81,90 +68,125 @@ import {eventOptions} from 'lit/decorators/event-options.js';
8168

8269
To use decorators, you need to build your code with a compiler such as [TypeScript](#decorators-typescript) or [Babel](#decorators-babel).
8370

84-
In the future when decorators become a native web platform feature, this may no longer be necessary.
71+
In the future when decorators are supported natively in browsers, this will no longer be necessary
8572

8673
### Using decorators with TypeScript { #decorators-typescript }
8774

88-
To use decorators with [TypeScript](https://www.typescriptlang.org/docs/handbook/decorators.html), enable the `experimentalDecorators` compiler option.
75+
TypeScript supports both experimental decorators and standard decorators. We recommend that TypeScript developers use experimental decorators for now for [optimal compiler output](#compiler-output-considerations). If your project requires using standard decorators or setting `"useDefineForClassFields": true`, skip down to [migrating to standard decorators](#migrating-typescript-standard-decorators).
76+
77+
To use experimental decorators you must enable the `experimentalDecorators` compiler option.
8978

90-
You should also ensure that the `useDefineForClassFields` setting is `false`. Note, this should only be required when the `target` is set to `esnext` or greater, but it's recommended to explicitly ensure this setting is `false`.
79+
You should also ensure that the `useDefineForClassFields` setting is `false`. Note, this is only required when the `target` is set to `ES2022` or greater, but it is recommended to explicitly set this to `false`.
9180

9281
```json
93-
"experimentalDecorators": true,
94-
"useDefineForClassFields": false,
82+
// tsconfig.json
83+
{
84+
"compilerOptions": {
85+
"experimentalDecorators": true,
86+
"useDefineForClassFields": false,
87+
}
88+
}
9589
```
9690

9791
Enabling `emitDecoratorMetadata` is not required and not recommended.
9892

99-
### Using decorators with Babel { #decorators-babel }
93+
#### Migrating TypeScript experimental decorators to standard decorators { #migrating-typescript-standard-decorators }
10094

101-
If you're compiling JavaScript with [Babel](https://babeljs.io/docs/en/), you can enable decorators by adding the following plugins and settings:
95+
Lit decorators are designed to support [standard decorator syntax](#standard-decorators) (using `accessor` on class field decorators) with TypeScript's experimental decorator mode.
10296

103-
* [`@babel/plugin-proposal-decorators`](https://babeljs.io/docs/en/babel-plugin-proposal-decorators)
104-
* [`@babel/plugin-proposal-class-properties`](https://babeljs.io/docs/en/babel-plugin-proposal-class-properties)
105-
106-
Note, the `@babel/plugin-proposal-class-properties` may not be required with the latest versions of Babel.
107-
108-
To set up the plugins, add code like this to your Babel configuration:
97+
This allows incremental migration off of experimental decorators starting with the addition of the `accessor` keyword to decorated properties without a change of behavior. Once all decorated class field use the `accessor` keyword, you can change your compiler options to complete the migration to standard decorators:
10998

11099
```json
111-
"assumptions": {
112-
"setPublicClassFields": true
113-
},
114-
"plugins": [
115-
["@babel/plugin-proposal-decorators", {
116-
"version": "2018-09",
117-
"decoratorsBeforeExport": true
118-
}],
119-
["@babel/plugin-proposal-class-properties"]
120-
]
100+
// tsconfig.json
101+
{
102+
"compilerOptions": {
103+
"experimentalDecorators": false, // default for TypeScript 5.0 and up
104+
"useDefineForClassFields": true, // default when "target" is "ES2022" or higher
105+
}
106+
}
121107
```
122108

123-
<div class="alert alert-info">
124-
125-
Babel decorator support has been tested with `version: '2018-09'`. This is currently the default, but we recommend setting the version explicitly in case the default changes. Other versions ('2021-12' or 'legacy') are not supported, but this may change as Babel evolves. See the [Babel documentation](https://babeljs.io/docs/en/babel-plugin-proposal-decorators#options) if you want to experiment.
109+
Note: The `accessor` keyword was introduced in TypeScript 4.9 and standard decorators with metadata require TypeScript ≥5.2.
126110

127-
</div>
111+
### Using decorators with Babel { #decorators-babel }
128112

129-
### Using decorators with TypeScript and Babel
113+
[Babel](https://babeljs.io/docs/en/) supports standard decorators with the [`@babel/plugin-proposal-decorators`](https://babeljs.io/docs/en/babel-plugin-proposal-decorators) plugin as of version 7.23. Babel does not support TypeScript experimental decorators, so you must use Lit decorators with [standard decorator syntax](#standard-decorators) using the `accessor` keyword on decorated class fields.
130114

131-
When using TypeScript with Babel, it's important to order the TypeScript transform before the decorators transform in your Babel config as follows:
115+
Enable decorators by adding [`@babel/plugin-proposal-decorators`](https://babeljs.io/docs/en/babel-plugin-proposal-decorators) with these Babel configuration settings:
132116

133117
```json
118+
// babel.config.json
134119
{
135-
"assumptions": {
136-
"setPublicClassFields": true
137-
},
138120
"plugins": [
139-
["@babel/plugin-transform-typescript", {
140-
"allowDeclareFields": true
141-
}],
142-
["@babel/plugin-proposal-decorators", {
143-
"version": "2018-09",
144-
"decoratorsBeforeExport": true
145-
}],
146-
["@babel/plugin-proposal-class-properties"]
121+
["@babel/plugin-proposal-decorators", {"version": "2023-05"}]
147122
]
148123
}
149124
```
150125

151-
The `allowDeclareFields` setting is generally not needed, but it can be useful if you want to define a reactive property without using a decorator. For example,
126+
Note: Lit decorators only work with `"version": "2023-05"`. Other versions, including the formerly supported `"2018-09"`, are not supported.
127+
128+
### Avoiding issues with class fields and decorators {#avoiding-issues-with-class-fields}
129+
130+
Standard [class fields](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Public_class_fields) have a problematic interaction with declaring reactive properties. See [Avoiding issues with class fields when declaring properties](/docs/v3/components/properties/#avoiding-issues-with-class-fields) for more information.
131+
132+
When using decorators, transpiler settings for Babel and TypeScript must be configured correctly as shown in the sections above for [TypeScript](#decorators-typescript) and [Babel](#decorators-babel).
133+
134+
## Decorator versions
135+
136+
Decorators are a [stage 3 proposal](https://github.com/tc39/proposal-decorators) for addition to the ECMAScript standard. Compilers like [Babel](https://babeljs.io/) and [TypeScript](https://www.typescriptlang.org/) support decorators, though no browsers have implemented them yet. Lit decorators work with Babel and TypeScript, and will work in browsers when they implement them natively.
137+
138+
{% aside "info" %}
139+
140+
What does stage 3 mean?
141+
142+
It means that the specification text is complete, and ready for browsers to implement. Once the specification has been implemented in multiple browsers, it can move to the final stage, stage 4, and be added to the ECMAScript standard. A stage 3 proposal will only change if critical issues are discovered during implementation.
143+
144+
{% endaside %}
145+
146+
### Earlier decorator proposals
147+
148+
Before the TC39 proposal reached stage 3, compilers implemented earlier versions of the decorator specification.
149+
150+
Most notable of these is [TypeScript's _experimental decorators_](https://www.typescriptlang.org/docs/handbook/decorators.html) which Lit has supported since its inception and is our current recommendation for use.
151+
152+
Babel has also supported different versions of the specification over time as can be seen from the [`"version"` option of the decorator plugin](https://babeljs.io/docs/babel-plugin-proposal-decorators#version). In the past, Lit 2 has supported the `"2018-09"` version for Babel users but that has now been dropped in favor of the _standard_ `"2023-05"` version described below.
153+
154+
### Standard decorators { #standard-decorators }
155+
156+
_Standard decorators_ is the version of decorators that has reached Stage 3 consensus at TC39, the body that defines ECMAScript/JavaScript.
157+
158+
Standard decorators are supported in TypeScript and Babel, with native browser coming in the near future.
159+
160+
The biggest difference between standard decorators and experimental decorators is that, for performance reasons, standard decorators cannot change the _kind_ of a class member – fields, accessors, and methods – being decorated and replaced, and will only produce the same kind of member.
161+
162+
Since many Lit decorators generate accessors, this means that the decorators need to be applied to accessors, not class fields.
163+
164+
To make this convenient, the standard decorator specification adds the `accessor` keyword to declare "auto-accessors":
165+
166+
```ts
167+
class MyClass {
168+
accessor foo = 42;
169+
}
170+
```
171+
172+
Auto-accessors create a getter and setter pair that read and write from a private field. Decorators can then wrap these getters and setters.
173+
174+
Lit decorators that work on class fields with experimental decorators – such as `@property()`, `@state()`, `@query()`, etc. – must be applied to accessors or auto-accessors with standard decorators:
152175

153176
```ts
154-
static properties = { foo: {} };
177+
@customElement('my-element')
178+
export class MyElement extends LitElement {
155179

156-
declare foo: string;
180+
@property()
181+
accessor greeting = 'Welcome';
157182

158-
constructor() {
159-
super();
160-
this.foo = 'bar';
161183
}
162184
```
163185

164-
### Avoiding issues with class fields and decorators {#avoiding-issues-with-class-fields}
186+
### Compiler output considerations
165187

166-
[Class fields](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Public_class_fields) have a problematic interaction with declaring reactive properties. See [Avoiding issues with class fields when declaring properties](/docs/v3/components/properties/#avoiding-issues-with-class-fields) for more information.
188+
Compiler output for standard decorators is unfortunately large due to the need to generate the accessors, private storage, and other objects that are part of the decorators API.
167189

168-
The current decorators [stage 3 proposal](https://github.com/tc39/proposal-decorators) does not directly address this issue, but it should be solved as the proposal evolves and matures.
190+
So we recommend that users who wish to use decorators, if possible, use TypeScript experimental decorators for now.
169191

170-
When using decorators, transpiler settings for Babel and TypeScript must be configured correctly as shown in the sections above for [TypeScript](#decorators-typescript) and [Babel](#decorators-babel).
192+
In the future the Lit team plans on adding decorator transforms to our optional Lit Compiler in order to compile standard decorators to a more compact compiler output. Native browser support will also eliminate the need for any compiler transforms at all.

0 commit comments

Comments
 (0)