When to create custom decorators in TypeScript
Decorators save you repetitive code and make your architecture declarative. Create one when you need to: apply the same logic to multiple classes/methods (logging, validation, cache), modify behavior without touching implementation, or add metadata for runtime reflection. Classic example: @Authorize('admin') is cleaner than checking roles in every endpoint. They're also useful for cross-cutting concerns (security, monitoring, transactions) that don't belong in business logic. If you're copying the same try-catch or timer in 10 methods, you need a decorator.
Types of decorators and their use cases
Class decorators: Modify constructors, useful for DI or registering classes (@Injectable, @Entity). Method decorators: Wrap methods, ideal for logging, cache, validation (@Log, @Cache). Property decorators: Modify properties, used for metadata or transformations (@Column, @Expose). Parameter decorators: Mark parameters, common in web frameworks (@Body, @Param). NestJS and Angular use all types; TypeORM focuses on class and property; in custom libraries, method decorators are most versatile.
How to name decorators for self-documentation
Use clear verbs or states: @Validate says what it does, @IsEmail what it verifies, @Authorized what it requires. Avoid generic names like @Do or @Handle. If the decorator receives parameters, the name should suggest what it expects: @Cache(60) (TTL in seconds), @Roles('admin', 'editor') (role strings). Common prefixes: Is- for boolean validations, Use- for applying middleware, Before/After for lifecycle hooks. Consistency within project: if you use @LogStart, don't use @TraceEnd for the pair; better @LogEnd.
Common mistakes when implementing decorators
Not preserving metadata: Some decorators overwrite existing metadata; use Reflect.defineMetadata with merge. Not binding this: If the decorator wraps a method, you lose class context; use arrow functions or .bind(this). Build time vs runtime execution: Decorators run when declaring the class, not when instantiating; for dynamic logic, return a function. Not typing parameters: @Custom(options: any) is a trap; define clear interfaces. Forgetting experimentalDecorators: In tsconfig.json it must be true. Not documenting behavior: A decorator without docstring is a black box.