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.
Fixes 'useDefineForClassFields' is not compatible with older targets
{
"compilerOptions": {
"target": "ES2022",
"useDefineForClassFields": false // This conflicts with ES2022
}
}{"compilerOptions": {"target": "ES2022","useDefineForClassFields": false // This conflicts with ES2022}}'useDefineForClassFields' is not compatible with older targets
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.