Description
🐛 Bug Report
When using a TypeScript configuration of "target": "ESNext"
creating elements with fast-foundation
and fast-element
results in attributes not responding to value updates.
For example, for a button modeled after the fast-button
implementation:
class Button extends FoundationButton {
@attr
public appearance: ButtonAppearance;
public connectedCallback(): void {
super.connectedCallback();
if (!this.appearance) {
this.appearance = ButtonAppearance.Outline;
}
}
}
If the button is initialized without the appearance attribute set in html, <my-button></my-button>
, then we expect the connectedCallback to run and update the appearance property and attribute. Checking buttonInstance.appearance
returns the value "outline" as expected but the attribute did not update and the expected style did not render. Checking the adoptedStylesheets shows that the additional stylesheet is not attached indicating the behavior did not run.
Investigating shows that the appearance property is set as an owned values, ie buttonInstance.hasOwnProperty('appearance') === true
. That value is shadowing the accessors for the appearance field defined by the decorator.
Stepping through the code it looks like setting ESNext
enables native class fields. The initializer for the class field in the above example runs and sets the appearance
property with the value undefined
during construction (didn't nail down the exact timing). Then future property updates manipulate the owned value instead of triggering the accessors on the prototype chain. Stepping into the appearance assignment in the connectedCallback shows that the accessors are not triggered.
💻 Repro or Code Sample
See expected behavior and current behavior.
🤔 Expected Behavior
When using ESNext, and specifically browser native class fields, I'd expect the same behavior as using "target": "ES2020" in my TypeScript configuration. The following shows a stackblitz webcontainer configured for ES2020 and when running shows a button with red text illustrating the appearance correctly propagated. It also shows the hasOwnProperty flag as returning false: https://stackblitz.com/edit/fast-es2020-bug-button?file=src%2Fmain.ts
😯 Current Behavior
The following stackblitz webcontainer is configured for ESNext and when running shows a button with black text as the appearance property does not receive the update. It also shows the hasOwnProperty flag as returning true: https://stackblitz.com/edit/fast-esnext-bug-button?file=src%2Fmain.ts
💁 Possible Solution
Configure the TypeScript compiler for a target
that does not allow ES2022 native browser class fields. For example, in tsconfig.json
set "target": "ES2020".
🔦 Context
I would like to leverage native browser features where possible. I may file a separate issue but it would be nice if the other fast builds compiled to a newer TypeScript target than ES6 and relied on consumers who felt like it to use babel, etc. to target older browsers.
Or maybe include both es6 and esnext / es2021 builds in packages so native browser async / await can be used, etc. Benefits are smaller bundle sizes (generator and generator polyfills aren't injected for async / await and other features that are native).
Edit: Looks like the interactions of class fields and accessors is unintuitive at first glance for lots of folks. They newer decorators proposals may have what's needed for attribute interactions.
🌍 Your Environment
- macOS Big Sur 11.5.1 (20G80)
- Version 92.0.4515.107 (Official Build) (x86_64)
- @microsoft/fast-foundation 2.6.2
- @microsoft/fast-foundation 1.4.1
- typescript 4.3.5
- webpack 5.47.1