This error occurs when you try to apply a TypeScript decorator to a language construct that doesn't support decorators. Decorators can only be applied to classes, methods, accessors, properties, and parameters—not to standalone functions, interfaces, type aliases, or variables.
The "Decorator cannot be applied to this kind of declaration" error appears when you attempt to use a decorator on an unsupported construct. TypeScript decorators are special declarations that can modify or observe classes and class members, but they have strict limitations about where they can be used. TypeScript supports five types of decorators, each with specific rules: 1. **Class decorators** - Applied to class declarations 2. **Method decorators** - Applied to class methods 3. **Accessor decorators** - Applied to getters/setters 4. **Property decorators** - Applied to class properties 5. **Parameter decorators** - Applied to method or constructor parameters You cannot apply decorators to standalone functions, arrow functions, interfaces, type aliases, variable declarations, function parameters (outside of class contexts), or exported namespace members. Additionally, decorators cannot be used in ambient contexts like declaration files (.d.ts) or declare blocks. The most common mistake is attempting to decorate a standalone function instead of a class method, or trying to use decorators on constructs that only exist at compile-time (like interfaces and type aliases).
If you're trying to decorate a standalone function, convert it to a class method:
// WRONG - decorators don't work on standalone functions
@Log
function calculateTotal(price: number): number {
return price * 1.1;
}
// Error: Decorator cannot be applied to this kind of declaration
// CORRECT - use a class method instead
class Calculator {
@Log
calculateTotal(price: number): number {
return price * 1.1;
}
}For utility functions, create a utility class:
class MathUtils {
@Measure
static calculatePercentage(value: number, total: number): number {
return (value / total) * 100;
}
}
// Usage
const result = MathUtils.calculatePercentage(45, 200);This pattern works for both instance and static methods.
Decorators only work on runtime constructs, not compile-time only types:
// WRONG - interfaces are compile-time only
@Serializable
interface User {
name: string;
email: string;
}
// Error: Decorator cannot be applied to this kind of declaration
// CORRECT - use a class instead
@Serializable
class User {
name: string;
email: string;
constructor(name: string, email: string) {
this.name = name;
this.email = email;
}
}If you need both a type and runtime behavior:
// Define interface for type checking
interface IUser {
name: string;
email: string;
}
// Implement with decoratable class
@Serializable
class User implements IUser {
constructor(
public name: string,
public email: string
) {}
}Variable declarations cannot be decorated. Use class properties instead:
// WRONG - can't decorate variables
@Observable
const counter = 0;
// Error: Decorator cannot be applied to this kind of declaration
// CORRECT - use class property
class AppState {
@Observable
counter = 0;
@Observable
private userName = "";
}For reactive state management patterns:
class Store {
@Track
private items: string[] = [];
@Computed
get itemCount(): number {
return this.items.length;
}
@Action
addItem(item: string): void {
this.items.push(item);
}
}Parameter decorators only work on class method and constructor parameters, not standalone functions:
// WRONG - standalone function parameter
function greet(@Validate name: string) {
console.log(`Hello, ${name}`);
}
// Error: Decorator cannot be applied to this kind of declaration
// CORRECT - class method parameter
class Greeter {
greet(@Validate name: string) {
console.log(`Hello, ${name}`);
}
}For constructor parameters:
class UserService {
constructor(
@Inject('Database') private db: Database,
@Inject('Logger') private logger: Logger
) {}
async findUser(@ValidateId userId: string) {
return this.db.query('users', userId);
}
}Note: Standard decorators (TypeScript 5.0+ without experimentalDecorators) don't support parameter decorators at all.
TypeScript 5.0 introduced standard decorators that have different rules than experimental decorators:
// tsconfig.json - Using experimental decorators (legacy)
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}Standard decorators (TypeScript 5.0+) without the flag:
- Don't support parameter decorators
- Don't support decorating function overloads
- Don't work with emitDecoratorMetadata
If you're using frameworks like Angular, NestJS, or TypeORM that require experimental decorators:
{
"compilerOptions": {
"target": "ES2022",
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}If using standard decorators:
// Use 'accessor' keyword for auto-accessors
class Example {
@tracked accessor count = 0; // Standard decorator syntax
}Decorators cannot be used in .d.ts files or ambient (declare) contexts:
// WRONG - in a .d.ts file
declare class User {
@Validate
name: string;
}
// Error: Decorator cannot be applied to this kind of declaration
// CORRECT - remove decorators from declaration files
declare class User {
name: string;
}If you need both type declarations and runtime decorators:
// types.d.ts - declarations only
declare class User {
name: string;
validate(): boolean;
}
// user.ts - implementation with decorators
export class User {
@Length(3, 50)
name: string;
@Validate
validate(): boolean {
// implementation
return true;
}
}Don't use decorators in declare module or declare global blocks either.
### Experimental vs Standard Decorators
TypeScript has two decorator implementations with different restrictions:
Experimental Decorators (pre-TypeScript 5.0, requires experimentalDecorators: true):
- Support all five decorator types including parameter decorators
- Work with emitDecoratorMetadata for reflection
- Used by Angular, NestJS, TypeORM, InversifyJS
- Based on the Stage 2 TC39 proposal
Standard Decorators (TypeScript 5.0+, default without the flag):
- Don't support parameter decorators
- Don't support emitDecoratorMetadata
- Can't change the kind of class member (method stays method, accessor stays accessor)
- Introduce accessor keyword for auto-accessors
- Based on the Stage 3 TC39 proposal
If you get decorator errors after upgrading to TypeScript 5.0+, check if you're using legacy decorators:
# Search for parameter decorators in your codebase
grep -r "@.*(" src/ | grep "parameter"### Decorator Execution Order
When multiple decorators are applied to class members, they execute in a specific order:
1. Instance members: Parameter decorators → Accessor/Property/Method decorators
2. Static members: Parameter decorators → Accessor/Property/Method decorators
3. Constructor: Parameter decorators
4. Class: Class decorator
Example:
@ClassDecorator
class Example {
@PropertyDecorator
prop = 1;
@MethodDecorator
method(@ParamDecorator arg: string) {}
constructor(@ParamDecorator param: number) {}
}
// Execution order:
// 1. @ParamDecorator (on method parameter)
// 2. @MethodDecorator
// 3. @PropertyDecorator
// 4. @ParamDecorator (on constructor parameter)
// 5. @ClassDecorator### Workarounds for Standalone Functions
If you absolutely need decorator-like behavior on standalone functions, use higher-order functions:
// Decorator-style wrapper for standalone functions
function measurePerformance<T extends (...args: any[]) => any>(
fn: T
): T {
return ((...args: any[]) => {
const start = performance.now();
const result = fn(...args);
const end = performance.now();
console.log(`Execution time: ${end - start}ms`);
return result;
}) as T;
}
// Usage
const calculateTotal = measurePerformance((price: number) => {
return price * 1.1;
});This gives decorator-like functionality without requiring class methods.
### Framework-Specific Considerations
Angular - Requires experimental decorators:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: '<h1>Hello</h1>'
})
export class AppComponent {}NestJS - Heavily uses parameter decorators:
@Controller('users')
export class UsersController {
@Get(':id')
findOne(@Param('id') id: string) {
return `User #${id}`;
}
}TypeORM - Entity decorators require experimental mode:
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
}All these frameworks require experimentalDecorators: true in tsconfig.json.
### Debugging Decorator Issues
Enable verbose compiler output to see decorator evaluation:
npx tsc --showConfig | grep -i decorator
npx tsc --listFiles | grep decoratorCheck which decorators are being evaluated:
function DebugDecorator(target: any, propertyKey?: string, descriptor?: PropertyDescriptor) {
console.log('Decorator applied to:', target, propertyKey, descriptor);
}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