This TypeScript error occurs when you apply a decorator to both the getter and setter of a class property. TypeScript only allows decorators on the first accessor (getter or setter) in document order, as decorators apply to the unified property descriptor that encompasses both accessors, not each declaration separately.
The error "Accessor decorator cannot be applied to getter and setter" (TS1207) happens when you try to apply a decorator to both the `get` and `set` accessor of the same property in a TypeScript class. In TypeScript, decorators applied to accessors operate on the Property Descriptor, which is a unified object that combines both the getter and setter for a property. Because a single Property Descriptor represents both accessors together, TypeScript enforces that decorators can only be applied to the first accessor that appears in your code. This is not a bug or limitation you can work around—it's a fundamental design constraint of how JavaScript property descriptors work. The decorator needs to modify the descriptor once for both accessors, not separately for each one.
The fix is simple: apply your decorator only to the first accessor (whichever appears first in your code).
Incorrect (causes TS1207):
class User {
private _age: number = 0;
@ValidateRange(0, 150)
get age() {
return this._age;
}
@ValidateRange(0, 150) // ❌ Error: Cannot decorate both
set age(value: number) {
this._age = value;
}
}Correct (decorator only on first accessor):
class User {
private _age: number = 0;
@ValidateRange(0, 150) // ✅ Single decorator on getter
get age() {
return this._age;
}
set age(value: number) {
this._age = value;
}
}The decorator will apply to the entire property descriptor, affecting both the getter and setter behavior. You do not need to repeat it on the setter.
When you apply a decorator to an accessor, it receives the Property Descriptor object, which contains both get and set methods.
How a property descriptor works:
// This is what JavaScript creates internally:
const descriptor = {
get() { /* getter logic */ },
set(value) { /* setter logic */ },
enumerable: true,
configurable: true
};
Object.defineProperty(obj, 'age', descriptor);A decorator receives this entire descriptor:
function MyDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
// descriptor.get is the getter function
// descriptor.set is the setter function
// You can modify BOTH through a single decorator
const originalGet = descriptor.get;
descriptor.get = function() {
console.log('Getting');
return originalGet.call(this);
};
return descriptor;
}
class Example {
@MyDecorator // Single decorator, applies to both get and set
get value() { return 42; }
set value(v) { /* setter logic */ }
}Because the decorator modifies the descriptor object which contains both accessors, applying it twice would be redundant and is explicitly forbidden by TypeScript.
Decide whether you want the decorator on the getter or setter depending on what you want to validate or modify.
Decorator on the getter (most common):
Use this if you want to intercept reads or validate the returned value:
class User {
private _password: string = '';
@LogAccess // Logs whenever the property is read
get password() {
return this._password;
}
set password(value: string) {
this._password = value;
}
}Decorator on the setter:
Use this if you want to validate writes or intercept assignments:
class User {
private _email: string = '';
get email() {
return this._email;
}
@ValidateEmail // Validates email before assignment
set email(value: string) {
this._email = value;
}
}In both cases, the decorator modifies the property descriptor that affects the entire property (both getter and setter).
Ensure your TypeScript configuration supports decorators, as they are an experimental feature.
Check tsconfig.json:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"target": "ES2020"
}
}Key settings:
- "experimentalDecorators": true - Required to use decorators
- "emitDecoratorMetadata": true - Optional, for reflection metadata
- "target": "ES2020" or higher - Decorators need a modern target
If you're upgrading from an older project, verify these are set. The TS1207 error will appear during type checking if decorators aren't properly enabled.
If you need to apply logic to both the getter and setter, use a single decorator that modifies both:
function ObserveChanges(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalGet = descriptor.get;
const originalSet = descriptor.set;
descriptor.get = function() {
console.log(`Getting ${propertyKey}`);
return originalGet?.call(this);
};
descriptor.set = function(value: any) {
console.log(`Setting ${propertyKey} to ${value}`);
originalSet?.call(this, value);
};
return descriptor;
}
class MyClass {
private _name: string = '';
@ObserveChanges // Single decorator handles both get and set
get name() {
return this._name;
}
set name(value: string) {
this._name = value;
}
}
// Usage:
const obj = new MyClass();
obj.name = 'Alice'; // Logs: "Setting name to Alice"
console.log(obj.name); // Logs: "Getting name"This pattern lets you apply unified logic to both the getter and setter without triggering the TS1207 error.
TypeScript 5.0 Auto-Accessors:
TypeScript 5.0 introduced auto-accessors, which provide a cleaner syntax for getter/setter pairs and work better with decorators:
class User {
@ValidateRange(0, 150)
accessor age: number = 0; // Auto-generates get and set
}Auto-accessors are syntactic sugar that automatically creates backing fields and getters/setters, with decorators applying to the entire accessor pair. This is the modern approach if you're on TypeScript 5.0+.
Property Descriptors and JavaScript Semantics:
In JavaScript, a Property Descriptor is an object that describes how a property behaves:
- Data property: Has value and writable
- Accessor property: Has get and set
A property cannot be both a data property and an accessor property. When you use getters and setters, JavaScript creates an accessor property with both get and set methods in a single descriptor. Decorators in TypeScript follow this pattern—they receive the descriptor and can modify either accessor, but they're applied once to the unified descriptor.
Decorator Chaining with Accessors:
If you need multiple decorators on accessors, apply all of them to the first accessor:
class Example {
@Logger
@Validator
@Observer
get value() { return 42; } // All decorators on first accessor
set value(v) { /* setter */ } // No decorators here
}All decorators will be applied in order, each receiving and modifying the same property descriptor.
Common Patterns in Frameworks:
- NestJS: Uses @Expose and @Transform on getters for serialization
- TypeORM: Uses @Column on getters for entity properties
- class-validator: Uses @IsEmail on getters to validate accessor values
In all these cases, the decorator is placed on the first accessor only.
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