Skip to content

Commit 7ca04b7

Browse files
author
Steve Orvell
authored
Beef up docs around using class fields (#516)
Beefs up docs around using class fields, fixes #462.
1 parent eba1ed0 commit 7ca04b7

File tree

4 files changed

+64
-40
lines changed

4 files changed

+64
-40
lines changed

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

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -73,25 +73,34 @@ In the future when decorators become a native web platform feature, this may no
7373

7474
To use decorators with [TypeScript](https://www.typescriptlang.org/docs/handbook/decorators.html), enable the `experimentalDecorators` compiler option.
7575

76+
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`.
77+
7678
```json
7779
"experimentalDecorators": true,
80+
"useDefineForClassFields": false,
7881
```
7982

8083
Enabling `emitDecoratorMetadata` is not required and not recommended.
8184

8285
### Using decorators with Babel { #decorators-babel }
8386

84-
If you're compiling JavaScript with [Babel](https://babeljs.io/docs/en/), you can enable decorators by adding the following plugins:
87+
If you're compiling JavaScript with [Babel](https://babeljs.io/docs/en/), you can enable decorators by adding the following plugins and settings:
8588

86-
* [`@babel/plugin-proposal-decorators`](https://babeljs.io/docs/en/babel-plugin-proposal-decorators).
89+
* [`@babel/plugin-proposal-decorators`](https://babeljs.io/docs/en/babel-plugin-proposal-decorators)
8790
* [`@babel/plugin-proposal-class-properties`](https://babeljs.io/docs/en/babel-plugin-proposal-class-properties)
8891

89-
To enable the plugins, add code like this to your Babel configuration:
92+
Note, the `@babel/plugin-proposal-class-properties` may not be required with the latest versions of Babel.
93+
94+
To set up the plugins, add code like this to your Babel configuration:
9095

9196
```js
97+
assumptions = {
98+
"setPublicClassFields": true
99+
};
100+
92101
plugins = [
93102
['@babel/plugin-proposal-decorators', {decoratorsBeforeExport: true}],
94-
["@babel/plugin-proposal-class-properties", {"loose": true}],
103+
["@babel/plugin-proposal-class-properties"],
95104
];
96105
```
97106

@@ -101,42 +110,19 @@ Currently the older `legacy` mode of Babel decorators is not supported, but this
101110

102111
</div>
103112

104-
### Avoiding issues with class fields
105-
106-
Class fields are a [stage 3 proposal](https://github.com/tc39/proposal-decorators) for addition to the ECMAScript standard. They currently have a problematic interaction with the decorators proposal in some circumstances.
107-
108-
There are generally no issues when using TypeScript. However, it's important to ensure that the `useDefineForClassFields` setting in your `tsconfig` is set to false. This is currently the default setting.
109-
110-
When using Babel, class fields should only be used for properties that are defined with a decorator.
111-
112-
<div class="alert alert-info">
113-
Using the `static properties` syntax along with class fields is not supported.
114-
</div>
115-
116-
The following is ok:
117-
118-
```js
119-
@property()
120-
foo = 'bar';
121-
```
122-
123-
but this is **not supported**:
124-
125-
```js
126-
static properties = { foo: {} };
127-
foo = 'bar';
128-
```
129-
130-
### Using TypeScript with Babel
113+
### Using decorators with TypeScript and Babel
131114

132115
When using TypeScript with Babel, it's important to order the TypeScript transform before the decorators transform in your Babel config as follows:
133116

134117
```js
135118
{
119+
"assumptions": {
120+
"setPublicClassFields": true
121+
},
136122
"plugins":[
137123
["@babel/plugin-transform-typescript", {"allowDeclareFields": true}],
138124
["@babel/plugin-proposal-decorators", {"decoratorsBeforeExport": true}],
139-
["@babel/plugin-proposal-class-properties", {"loose": true}],
125+
["@babel/plugin-proposal-class-properties"],
140126
]
141127
}
142128
```
@@ -153,3 +139,11 @@ constructor() {
153139
this.foo = 'bar';
154140
}
155141
```
142+
143+
### Avoiding issues with class fields and decorators {#avoiding-issues-with-class-fields}
144+
145+
[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/components/properties/#avoiding-issues-with-class-fields) for more information.
146+
147+
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.
148+
149+
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).

packages/lit-dev-content/site/docs/components/properties.md

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,34 @@ class MyElement extends LitElement {
115115

116116
An empty option object is equivalent to specifying the default value for all options.
117117

118-
<div class="alert alert-info">
118+
### Avoiding issues with class fields when declaring properties {#avoiding-issues-with-class-fields}
119119

120-
**If you're using the static properties field, initialize properties in the constructor**. Class field initializers won't work in this case.
120+
[Class fields](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Public_class_fields) have a problematic interaction with reactive properties. Class fields are defined on the element instance. Reactive properties are defined as accessors on the element prototype. According to the rules of JavaScript, an instance property takes precedence over and effectively hides a prototype property. This means that reactive property accessors do not function when class fields are used. When a property is set, the element does not update.
121121

122-
</div>
122+
In **JavaScript** you **must not use class fields** when declaring reactive properties. Instead, properties must be initialized in the element constructor:
123+
124+
```js
125+
constructor() {
126+
super();
127+
this.data = {};
128+
}
129+
```
130+
131+
For **TypeScript**, you **may use class fields** for declaring reactive properties as long as the `useDefineForClassFields` setting in your `tsconfig` is set to `false`. Note, this is not required for some configurations of TypeScript, but it's recommended to explicitly set it to `false`.
132+
133+
When compiling JavaScript with **Babel**, you **may use class fields** for declaring reactive properties as long as you set `setPublicClassFields` to `true` in the `assumptions` config of your `babelrc`. Note, for older versions of Babel, you also need to include the plugin `@babel/plugin-proposal-class-properties`:
134+
135+
```js
136+
assumptions = {
137+
"setPublicClassFields": true
138+
};
139+
140+
plugins = [
141+
["@babel/plugin-proposal-class-properties"],
142+
];
143+
```
144+
145+
For information about using class fields with **decorators**, see [Avoiding issues with class fields and decorators](/docs/components/decorators/#avoiding-issues-with-class-fields).
123146

124147
### Property options
125148

packages/lit-dev-content/site/docs/tools/publishing.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,13 @@ The following JSON sample is a partial `tsconfig.json` that uses recommended opt
4646
"lib": ["es2019", "dom"],
4747
"declaration": true,
4848
"declarationMap": true,
49-
"experimentalDecorators": true
49+
"experimentalDecorators": true,
50+
"useDefineForClassFields": false
5051
}
5152
```
5253

54+
Note, setting `useDefineForClassFields` to `false` should only be required when the `target` is set to `esnext` or greater, but it's recommended to explicitly ensure this setting is `false`.
55+
5356
When compiling from TypeScript, you should include declaration files
5457
(generated based on `declaration: true` above) for your component's types in the
5558
`types` field of `package.json`, and ensure the `.d.ts` and `.d.ts.map` files
@@ -82,12 +85,17 @@ Configure Babel. For example:
8285
**babel.config.js**
8386

8487
```js
88+
const assumptions = {
89+
"setPublicClassFields": true
90+
};
91+
8592
const plugins = [
86-
["@babel/plugin-proposal-class-properties", {"loose": true}],
8793
['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true } ],
94+
["@babel/plugin-proposal-class-properties"],
95+
8896
];
8997

90-
module.exports = { plugins };
98+
module.exports = { assumptions, plugins };
9199
```
92100

93101
You can run Babel via a bundler plugin such as [@rollup/plugin-babel](https://www.npmjs.com/package/@rollup/plugin-babel), or from the command line. See the [Babel documentation](https://babeljs.io/docs/en/) for more information.

packages/lit-dev-server/src/redirects.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ export const pageRedirects = new Map([
1010
// TODO(sorvell) https://github.com/lit/lit.dev/issues/455
1111
['/msg/multiple-versions', '/docs/tools/requirements/'],
1212
['/msg/polyfill-support-missing', '/docs/tools/requirements/#polyfills'],
13-
// TODO(sorvell) https://github.com/lit/lit.dev/issues/462
14-
['/msg/class-field-shadowing', '/docs/components/properties/#declare'],
13+
['/msg/class-field-shadowing', '/docs/components/properties/#avoiding-issues-with-class-fields'],
1514
// TODO(aomarks) Should we add something specifically about this issue?
1615
['/msg/change-in-update', '/docs/components/properties/#when-properties-change'],
1716
['/msg/deprecated-import-path', '/docs/releases/upgrade/#update-packages-and-import-paths'],

0 commit comments

Comments
 (0)