Decorators (Practical Use)
What are decorators?
Decorators are a meta-programming feature that lets you attach behavior or metadata to classes, methods, properties, or parameters. They are widely used in certain ecosystems (for example, dependency injection and validation in backend frameworks) but they are not required for most TypeScript projects.
Why decorators are controversial
Decorators can make code feel magical because behavior is applied indirectly. This can reduce clarity, complicate debugging, and make refactoring harder. In production systems, decorators are best used when they provide clear, measurable value and when the team understands the pattern well.
Where decorators are commonly used
- Dependency injection and service registration.
- Validation and transformation rules on DTOs.
- Routing metadata in framework-style controllers.
- Logging, tracing, and cross-cutting concerns.
A minimal decorator example
This example shows a simple class decorator that adds metadata.
function Tagged(tag: string) {
return function (constructor: Function) {
(constructor as any).tag = tag;
};
}
@Tagged("service")
class UserService {}
console.log((UserService as any).tag);
Method decorator example
A method decorator can wrap a function for logging or timing. Keep these patterns explicit and predictable.
function LogCall() {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const original = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log("Calling", propertyKey, args);
return original.apply(this, args);
};
};
}
class Calculator {
@LogCall()
add(a: number, b: number) {
return a + b;
}
}
Configuration considerations
Decorators typically require TypeScript compiler options. Projects that use decorators must align configuration across tooling and build environments. If your project does not need decorators, keep them disabled to reduce complexity.
Use decorators at boundaries
If you use decorators, keep them near boundaries: controllers, DTOs, infrastructure integration points. Avoid spreading decorator-driven logic deep into core domain code, where explicitness is more valuable.
Prefer explicit code for business logic
Decorators are best for cross-cutting concerns. Business rules should remain explicit and testable, not hidden behind metadata or implicit behavior.
Common mistakes
- Using decorators for simple tasks that could be plain functions.
- Creating hidden side effects that are hard to trace.
- Overusing any inside decorator implementations.
- Building a framework-like layer without clear need.
Production guidance
- Use decorators only when they clearly simplify repeated cross-cutting concerns.
- Keep decorator behavior small, predictable, and well-documented.
- Avoid decorators in core domain logic.
- Prefer explicit composition unless a framework ecosystem strongly benefits from decorators.
What’s next
Next, we will cover namespaces: what they were designed for, why modern projects often avoid them, and when they can still be appropriate.