This TypeScript configuration error occurs when useDefineForClassFields conflicts with your target setting. The option defaults to true for ES2022+ but false for older targets, causing compatibility issues when explicitly mismatched.
This error appears when there's a mismatch between your tsconfig.json's `target` setting and the `useDefineForClassFields` compiler option. TypeScript introduced this flag to align with the TC39 class fields specification. The `useDefineForClassFields` option controls how class field initializers are compiled. When set to `true`, TypeScript uses JavaScript's [[Define]] semantics (the standard behavior), where field initializers don't trigger setters on base classes. When `false`, it uses TypeScript's legacy [[Set]] semantics where initializers do trigger setters. The incompatibility arises because TypeScript automatically sets `useDefineForClassFields` to `true` when target is ES2022 or later, but defaults to `false` for ES2021 and earlier targets. Explicitly setting it opposite to what the target expects creates a configuration conflict that TypeScript flags as incompatible.
Open your tsconfig.json file and locate both the target and useDefineForClassFields options:
{
"compilerOptions": {
"target": "ES2022",
"useDefineForClassFields": false // This conflicts with ES2022
}
}Note the current values to understand the mismatch.
For ES2022 or newer targets, either remove useDefineForClassFields (let it default to true) or explicitly set it to true:
{
"compilerOptions": {
"target": "ES2022",
"useDefineForClassFields": true // Aligned with ES2022
}
}For ES2021 or older targets, either remove the option (defaults to false) or set it to false:
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": false // Aligned with ES2020
}
}If you're using decorators with ES2022+ target, you'll need to migrate your class fields. Use the declare keyword for fields managed by decorators:
// Before (breaks with useDefineForClassFields: true)
class MyComponent {
@Input() name: string;
}
// After (works with useDefineForClassFields: true)
class MyComponent {
declare @Input() name: string;
}Or keep target at ES2021 or lower if extensive decorator refactoring isn't feasible.
Some build tools override TypeScript settings. For Angular CLI:
// angular.json
{
"projects": {
"your-app": {
"architect": {
"build": {
"options": {
"target": ["es2022"] // CLI may override tsconfig
}
}
}
}
}
}Check your build tool documentation and align both the tool config and tsconfig.json.
After adjusting settings, verify class field initialization works as expected:
class Base {
set value(v: number) {
console.log('Setter called');
}
}
class Derived extends Base {
value = 42; // Will trigger setter if useDefineForClassFields: false
}
new Derived(); // Check console outputWith useDefineForClassFields: true, the setter won't be called. With false, it will be.
Clean your build artifacts and recompile:
# Remove build cache
rm -rf dist/ node_modules/.cache/
# Rebuild
npm run build
# or
tsc --build --clean && tsc --buildVerify there are no configuration errors and your application behaves correctly.
Understanding [[Define]] vs [[Set]] Semantics:
The fundamental difference between the two modes affects inheritance:
- [[Define]] (true): Creates a new own property directly, bypassing setters
- [[Set]] (false): Uses property assignment, which invokes setters in the prototype chain
Migration Strategy for Large Codebases:
If you have a large codebase using decorators (Angular, TypeORM, MobX, etc.):
1. Stay on target ES2021 or lower until you can refactor
2. Audit decorator usage with tools like tsc --noEmit
3. Migrate incrementally, testing each module
Framework-Specific Considerations:
- Angular: Angular 15+ requires ES2022, which forced many projects to add declare keywords to decorated properties
- Vite: Automatically handles this via esbuild, but respects tsconfig settings
- esbuild: Follows TypeScript's rules exactly - target ES2022 → useDefineForClassFields: true
Performance Implications:
Setting useDefineForClassFields: true can be slower because it uses Object.defineProperty() instead of simple assignment. For most applications this is negligible, but in tight loops initializing many class instances, it may be measurable.
Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript