TypeScript Decorators
debt(d5/e3/b7/t7)
Closest to 'specialist tool catches it' (d5). The detection_hints list TypeScript compiler and ESLint. Missing experimentalDecorators causes a TS compiler error (which is caught), but mixing legacy and TC39 standard decorators or forgetting reflect-metadata import at the entry point can produce runtime failures that the compiler won't always catch clearly. The code_pattern notes mixing legacy and standard decorators in the same project, which requires careful tooling configuration to surface — landing at d5.
Closest to 'simple parameterised fix' (e3). The quick_fix is essentially a tsconfig flag change (experimentalDecorators: true) or adding a reflect-metadata import — both are small, localised changes. However, if a project has mixed legacy and TC39 standard decorators, untangling them touches multiple files. The common cases lean toward e3, with mixed-decorator scenarios pushing slightly higher but not reaching e5 consistently.
Closest to 'strong gravitational pull' (b7). Decorators are the foundational abstraction for major frameworks (NestJS, Angular, TypeORM) as noted in why_it_matters. Once a codebase adopts decorator-heavy frameworks, the decorator model shapes every service, entity, and component definition. The incompatibility between legacy and TC39 standard decorators (TS 5.0+) means this choice reverberates across the entire project's tsconfig, dependency versions, and framework compatibility — every future change is shaped by which decorator model was chosen.
Closest to 'serious trap' (t7). The misconception field explicitly states that developers familiar with Python decorators will guess wrong — Python decorators are simple function wrappers, while TypeScript decorators have a specific API, rely on reflect-metadata, and run at class definition time (not instantiation). Additionally, the legacy vs. TC39 standard decorator incompatibility contradicts what a developer might expect from a newer TypeScript version. These are documented gotchas that contradict behavior from analogous concepts in other languages, placing this at t7.
Also Known As
TL;DR
Explanation
TypeScript decorators (TC39 Stage 3 in 2024) annotate classes and members with @Decorator syntax. Class decorators modify or replace the class. Method decorators wrap methods. Property decorators define metadata on properties. Parameter decorators annotate constructor/method parameters. Decorator metadata: reflect-metadata stores type information accessed at runtime, enabling dependency injection (NestJS @Injectable), ORM mapping (TypeORM @Entity, @Column), and validation (class-validator @IsEmail). TypeScript 5.0 implements the TC39 decorator proposal; legacy decorators (experimentalDecorators) differ.
Common Misconception
Why It Matters
Common Mistakes
- Not enabling emitDecoratorMetadata and experimentalDecorators in tsconfig for legacy decorators.
- Forgetting to import reflect-metadata at the entry point for metadata decorators.
- Using legacy decorators (experimentalDecorators) with TS 5.0 new decorators — they are incompatible.
- Decorators that have side effects during class definition — decorators run at class definition time, not instantiation.
Code Examples
// Manual DI wiring without decorators — verbose:
const userService = new UserService(new UserRepository(new Database()));
const orderService = new OrderService(userService, new OrderRepository(new Database()));
// Every constructor dependency must be manually wired
// NestJS decorators — declarative DI:
@Injectable()
export class UserService {
constructor(private readonly userRepo: UserRepository) {}
}
@Entity('users')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ unique: true })
@IsEmail()
email: string;
}
@Controller('users')
export class UserController {
constructor(private readonly userService: UserService) {}
@Get(':id')
findOne(@Param('id') id: number): Promise<User> {
return this.userService.findById(id);
}
}