Description
Bug Report
useDefineForClassFields
defaults to true
when target is set to es2022 or esnext. This is breaking lots of projects that use decorators.
This was reported in #45584, but that issue was closed apparently because the title didn't include which versions of TypeScript set useDefineForClassFields
and because the TypeScript playground doesn't exhibit this same behavior. I've included a reproduction for tsc
here.
This behavior is not documented anywhere in the TypeScript docs that I can find.
🔎 Search Terms
useDefineForClassFields decorators
🕗 Version & Regression Information
Since 4.2.4? according to #45584
Present in 4.6.3 and 4.7.0-dev.20220422
⏯ Playground Link
This playground link does not demonstrate the problem, as it doesn't default useDefineForClassFields
like tsc
does. But if you set useDefineForClassFields
to true
you will see that that decorators could not work because the field is not an instance property that shadows the decorator defined prototype accessors.
Playground link with relevant code
This git repo reproduces the problem locally: https://github.com/justinfagnani/ts-decorators-bug
💻 Code
This does not work with target es2022
:
const observe = (proto: Object, name: PropertyKey) => {
const key = Symbol();
Object.defineProperty(proto, name, {
get() {
return this[key];
},
set(v: unknown) {
this[key] = v;
this.onChange?.(name, v);
}
});
};
class A {
@observe
foo = 'abc';
onChange(name: string, v: unknown) {
console.log(`${name} changed to ${v}`);
}
}
const a = new A();
a.foo = 'x'
🙁 Actual behavior
useDefineForClassFields
defaults to true
🙂 Expected behavior
Option 1: useDefineForClassFields
defaults to false
like for other permutations of module
and target
until the new behavior is documented and users are notified with a prominent breaking change notice on the decorators documentation and the release notes.
Option 2: Use constructor initialization for decorated properties even if useDefineForClassFields
is true
. This will keep projects from breaking regardless of the config.
Option 3: Disallow both useDefineForClassFields: true
and experimentalDecorators
in the same config.
This behavior is breaking a large number of our users. Sometimes they are in control of their own tsconfig and can set useDefineForClassFields
to false
, but sometimes the config is generated for them via some other tool and they don't know how to set the value. In this case their project is just broken and they blame our library, not the update to TypeScript that included the undocumented breaking change.