Repository navigation
useDefineForClassFields defaults to true when target is es2022, breaks decorators. #48814
Description
Activity
andrewbranch commented
on Apr 22, 2022 MemberMore actionsThis behavior is not documented anywhere in the TypeScript docs that I can find.
In the tsconfig reference:
And in the release notes:
Open to adding something to the Decorators docs. We discussed other options in a design meeting and the only one we were sort of ok with was issuing a config diagnostic for
experimentalDecorators. (In fact, this is exactly what I said here.)- addedSuggestionAn idea for TypeScriptAn idea for TypeScriptIn DiscussionNot yet reached consensusNot yet reached consensus
on Apr 22, 2022 justinfagnani commented
on Apr 22, 2022 AuthorMore actionsI guess I missed those and was looking in the decorator docs, don't know why I didn't see the 4.3 notes mention.
Why can't decorated fields opt-out of define semantics? We're seeing more and more users that are broken, and they get no errors at all. They're just assuming our library broke when it didn't change.
andrewbranch commented
on Apr 22, 2022 MemberMore actionsUnfortunately the design notes only capture “That sounds horrifying to me” in answer to that question. From my memory, I think the sentiment was generally that experimental decorators are experimental, so we’re not going to break standards compliance elsewhere to make them keep working, and that users should simply not try to use them in combination with useDefineForClassFields, making the config diagnostic the only sensible mitigation.
Reacted by Martin Johns, Andrii Dieiev, Rob Palmer, Toni Villena, Ryan Cavanaugh, Ryan Ju and Alexey NosovReacted by Euler Chavez and StNekromanuseDefineForClassFieldsdoesn't break "decorators", per se, they break this specific use of a decorator. Native decorators, per the Stage 3 spec, also cannot replace a field with an accessor. Instead, the Stage 3 decorators proposal introduces a newaccessorkeyword that performs the syntactic transformation of a field into an accessor for you:class C { accessor foo = 'abc'; } // becomes class C { #foo_backing_store = 'abc'; get foo() { return this.#foo_backing_store; } set foo(v) { this.#foo_backing_store = v; } }
It will be a bit before TS support for Stage 3 Decorators is ready, but I have been considering introducing the
accessorkeyword to TS independently.The other approach would be to recommend your users use
declareif they have no control over theuseDefineForClassFieldssetting:class C { @observe declare foo: string; // field not declared on class, but decorator will still run constructor() { this.foo = "abc"; } }
declareon a field in TS does not introduce a runtime field declaration underuseDefineForClassFields, it merely adds it to the type in TS. Adeclarefield cannot have an initializer, however, so it must be moved to the body of the constructor.Reacted by Erin Murphy and StNekromanjustinfagnani commented
on May 17, 2022 AuthorMore actionsuseDefineForClassFieldsbreaks code that otherwise works with it set to false. Whether that's technically breaking decorators or not is pretty inside baseball that isn't so useful to our customers.I know that we can tell users to use
declareif they hit this problem, but we also tell them to setuseDefineForClassFieldsto false. This doesn't help if they don't make their way to that documentation. More often their components silently don't work and they don't know why.A property descriptor isn't provided to a field decorator because there's no descriptor to modify. This decorator breaks because it uses
Object.definePropertyto introduce a new property that ends up shadowed by the field. Unfortunately, this was never something field decorators were designed to do. TS field decorators primarily existed as a means of recording metadata about a field. Performing mutations on the prototype outside of that capability is unsafe.Reacted by StNekroman- addedWorking as IntendedThe behavior described is the intended behavior; this is not a bugThe behavior described is the intended behavior; this is not a bugand removedIn DiscussionNot yet reached consensusNot yet reached consensusSuggestionAn idea for TypeScriptAn idea for TypeScript
on May 18, 2022 typescript-bot commented
on May 21, 2022 ContributorMore actionsThis issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.
Reacted by Khalid Ait Bellahs, Alfred, Fyodor and StNekromanReacted by Justin Fagnani, Alfred and StNekromanjustinfagnani commented
on May 22, 2022 AuthorMore actionsThis is really unfortunate, IMO. The switching of this setting to default true in some cases is actively breaking our users.
Reacted by Khalid Ait Bellahs, mjellings-wisc, Ivan Thomson, Alfred, Alexey Nosov, Fyodor, q_h and Erin Murphyandrewbranch commented
on May 23, 2022 MemberMore actionsI tried to get some movement on this in another design meeting. I asked Ron Buckton (@rbuckton) to update this issue with the summary of what we talked about; it looks like the design meeting notes aren’t up yet.
- locked as resolved and limited conversation to collaborators
on Oct 22, 2025


Bug Report
useDefineForClassFieldsdefaults totruewhen 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
useDefineForClassFieldsand because the TypeScript playground doesn't exhibit this same behavior. I've included a reproduction fortschere.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
useDefineForClassFieldsliketscdoes. But if you setuseDefineForClassFieldstotrueyou 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:🙁 Actual behavior
useDefineForClassFieldsdefaults totrue🙂 Expected behavior
Option 1:
useDefineForClassFieldsdefaults tofalselike for other permutations ofmoduleandtargetuntil 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
useDefineForClassFieldsistrue. This will keep projects from breaking regardless of the config.Option 3: Disallow both
useDefineForClassFields: trueandexperimentalDecoratorsin 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
useDefineForClassFieldstofalse, 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.