This TypeScript error occurs when you try to use decorators in unsupported contexts like class expressions, anonymous classes, or ambient declarations. The fix involves restructuring your code to use decorators only on named class declarations, methods, properties, accessors, or parameters.
TypeScript decorators are a special syntax that allows you to annotate and modify classes and their members. However, decorators can only be used in specific syntactic contexts. When you try to apply a decorator in an unsupported location, TypeScript emits error TS1206: "Decorators are not valid here." This error typically occurs in several situations: 1. **Class expressions**: When you use decorators on methods or properties inside a class that's defined as an expression (like returning an anonymous class from a function or instantiating a class inline) 2. **Ambient declarations**: When you try to use decorators in .d.ts declaration files or in 'declare' blocks 3. **Overload signatures**: When decorators are applied to function/method overload signatures rather than the implementation Decorators are only valid on: - Named class declarations (not expressions) - Class methods (in named classes) - Class properties (in named classes) - Class accessors (get/set) (in named classes) - Method/constructor parameters (in named classes) The restriction exists because decorators need to attach metadata to a stable, named construct that exists at runtime. Anonymous classes and ambient declarations don't meet these requirements.
If you're using decorators inside a class expression, convert it to a named class declaration first:
// WRONG - Decorator in instantiated class expression
export default new class {
@someDecorator
someMethod() {
return "hello";
}
}
// CORRECT - Define class separately, then instantiate
class MyClass {
@someDecorator
someMethod() {
return "hello";
}
}
export default new MyClass();For factory functions returning classes with decorators:
// WRONG - Anonymous class with decorators
function createMixin() {
return class {
@log
doSomething() {}
};
}
// CORRECT - Named class declaration
function createMixin() {
class Mixin {
@log
doSomething() {}
}
return Mixin;
}Decorators cannot be used in .d.ts files or 'declare' blocks. Remove them from type declarations:
// WRONG - Decorators in declaration file (.d.ts)
declare class MyService {
@inject(HttpClient)
constructor(http: HttpClient);
@cache()
getData(): Promise<Data>;
}
// CORRECT - Plain type declarations only
declare class MyService {
constructor(http: HttpClient);
getData(): Promise<Data>;
}If you need decorator metadata for third-party libraries, put the actual implementation in a .ts file:
// my-service.ts - Implementation with decorators
import { inject, cache } from 'my-framework';
export class MyService {
@inject(HttpClient)
constructor(http: HttpClient) {}
@cache()
getData(): Promise<Data> {
// implementation
}
}Decorators should be placed on the implementation signature, not on overload signatures:
// WRONG - Decorator on overload signature
class Calculator {
@validate
add(a: number, b: number): number;
@validate
add(a: string, b: string): string;
add(a: any, b: any): any {
return a + b;
}
}
// CORRECT - Decorator on implementation only
class Calculator {
add(a: number, b: number): number;
add(a: string, b: string): string;
@validate
add(a: any, b: any): any {
return a + b;
}
}For parameter decorators on overloads:
// WRONG - Parameter decorator on overload
class Service {
process(@validate data: string): void;
process(@validate data: number): void;
process(data: any): void {
// implementation
}
}
// CORRECT - Parameter decorator on implementation
class Service {
process(data: string): void;
process(data: number): void;
process(@validate data: any): void {
// implementation
}
}Ensure your tsconfig.json has decorator support enabled:
For legacy decorators (most frameworks still use this):
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"target": "ES2017"
}
}For TypeScript 5.0+ native decorators (no flag needed):
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022"]
}
}Note: Most frameworks (Angular, NestJS, TypeORM) still require experimentalDecorators: true. Native decorators in TS 5.0+ are a different, incompatible implementation.
In Angular and NestJS, decorators must be on properly exported classes:
// WRONG - Decorator placement in module
export const routes = [
{ path: '/', component: HomeComponent }
];
@NgModule({
declarations: [AppComponent],
imports: [RouterModule.forRoot(routes)]
})
export class AppModule {}
// CORRECT - Routes defined before decorator
const routes = [
{ path: '/', component: HomeComponent }
];
@NgModule({
declarations: [AppComponent],
imports: [RouterModule.forRoot(routes)]
})
export class AppModule {}For NestJS controllers:
// WRONG - Anonymous controller class
export const UserController = new class {
@Get()
getUsers() {
return [];
}
}
// CORRECT - Named exported class
@Controller('users')
export class UserController {
@Get()
getUsers() {
return [];
}
}TypeScript only allows decorators on the first accessor (get or set) of a property:
// WRONG - Decorators on both get and set
class User {
private _name: string = '';
@log
get name() {
return this._name;
}
@log // Error: Decorators are not valid here
set name(value: string) {
this._name = value;
}
}
// CORRECT - Decorator on first accessor only (typically the getter)
class User {
private _name: string = '';
@log
get name() {
return this._name;
}
set name(value: string) {
this._name = value;
}
}The decorator applies to the property descriptor as a whole, so you only need it once.
### TypeScript 5.0+ Decorator Changes
TypeScript 5.0 introduced a new decorator implementation aligned with the TC39 ECMAScript proposal. These new decorators:
- Don't require the experimentalDecorators flag
- Use a different signature (receive a context object)
- Are NOT compatible with existing decorator libraries
Example of new-style decorator:
// TypeScript 5.0+ decorator
function logged<T>(
value: Function,
context: ClassMethodDecoratorContext<T>
) {
const methodName = String(context.name);
return function (this: T, ...args: any[]) {
console.log(`Calling ${methodName}`);
return value.call(this, ...args);
};
}
class Calculator {
@logged
add(a: number, b: number) {
return a + b;
}
}Most frameworks (Angular, NestJS, TypeORM, etc.) still require experimentalDecorators: true and won't work with TS 5.0+ decorators.
### Class Expression vs Class Declaration
Understanding the difference helps avoid decorator errors:
// Class DECLARATION (decorators allowed)
class MyClass {
@decorator
method() {}
}
// Class EXPRESSION (decorators NOT allowed on members)
const MyClass = class {
@decorator // TS1206 error
method() {}
};
// IIFE class expression (decorators NOT allowed)
export default new class {
@decorator // TS1206 error
method() {}
}### Decorator Evaluation Order
When multiple decorators are used, they're evaluated in a specific order:
@classDecorator
class Example {
@propertyDecorator
prop: string = "test";
@methodDecorator
method(@paramDecorator param: string) {}
}
// Evaluation order:
// 1. Property decorators (top to bottom)
// 2. Parameter decorators (left to right)
// 3. Method decorators (top to bottom)
// 4. Class decorators (last)### Mixin Pattern with Decorators
If you need decorators in mixin classes:
// Define mixin as named class
class LoggingMixin {
@log
logMessage(msg: string) {
console.log(msg);
}
}
// Apply mixin to target class
function applyMixins(targetClass: any, mixins: any[]) {
mixins.forEach(mixin => {
Object.getOwnPropertyNames(mixin.prototype).forEach(name => {
targetClass.prototype[name] = mixin.prototype[name];
});
});
}
class MyClass {}
applyMixins(MyClass, [LoggingMixin]);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