ProgrammingFullstack Developer

How does the decorator pattern work in TypeScript? What are they used for, how to implement a custom decorator, and what pitfalls are encountered when working with them?

Pass interviews with Hintsage AI assistant

Answer.

Decorators are special annotations that allow modifying the behavior of classes, properties, methods, or parameters through additional metadata or by overriding implementations.

They are usually used for dependency injection, logging, validation, caching, or declaring metadata for frameworks like Angular.

Example of a simple method decorator:

function Log(target: any, propertyKey: string, descriptor: PropertyDescriptor) { const original = descriptor.value; descriptor.value = function(...args: any[]) { console.log(`Call: ${propertyKey}(${JSON.stringify(args)})`); return original.apply(this, args); }; } class Example { @Log doSomething(a: number, b: number) { return a + b; } }

Pitfalls:

  • Decorators work only if experimentalDecorators is enabled in tsconfig.
  • Incompatible with some strict mode settings.
  • Decorators do not always execute in the expected order; it is important to understand the call stack.
  • Parameter decorators do not allow changing their values directly.

Trick Question.

Question: "Is it possible to type the decorator parameters in such a way as to guarantee the type of the method to which it is applied?"

Answer: In most cases, typing is not possible at the time of applying the decorator due to the dynamic nature of metaprogramming. However, through generics and additional runtime checks, validation can be added, but the TypeScript compiler does not provide guarantees at the declaration stage of the structure.

Example:

function MyDecorator(target: any, propertyKey: string, descriptor: TypedPropertyDescriptor<number>) { /* ... */ } // The compiler does not guarantee that the method returns a number

Examples of real mistakes due to ignorance of the nuances.


Story

In a project with a large number of decorators on services, the order of application was neglected: property decorators were applied before all parameters, which led to the loss of Inject metadata and the inability to resolve dependencies at runtime.


Story

Developers decided to apply decorators to regular functions instead of class methods and were surprised by compilation errors and lack of runtime effect. In the TypeScript specification, decorators are applied only to classes and their members.


Story

After updating TypeScript versions and experimental settings, the entire decorator mechanism in the custom DI container broke: it turned out that the new version requires explicit support for Reflect-metadata, otherwise all metadata was lost.