Skip to main content
Our Hunters, technology trend hunters, never stop. In this new edition of Inspiring Technology, they talk about Decorators, within TypeScript 5.0. Keep reading!
24/01/2024
typescript

The release of TypeScript 5.0 includes a new standard for the use of Decorators. Decorators are a TypeScript feature that allows you to modify the behavior of classes and their members in a reusable way . They are based on the design pattern of the same name, which we explain in this article.

Discovering the Decorator design pattern

The Decorator design pattern is a structural design pattern that allows you to dynamically add functionality to a base object . This addition of functionality is accomplished by wrapping the base object in an encapsulating object that provides the added behaviors.

As an example of the Decorator pattern, you could consider an interface that displays a document like the following:

inspiring

And you could use the Decorator pattern to add the functionality to present the document in different formats as follows:

inspiring

Main features and uses

Decorators in TypeScript can be used on components such as properties/fields, getters, setters, class methods, classes, and auto-accessors, which we'll explain later. A decorator can perform up to four functions , depending on the type of component it's applied to:

  • Make changes to the entity to be decorated.
  • Replace the entity to be decorated with another of the same type.
  • Expose access to the entity to be decorated to third parties.
  • Process the entity to be decorated and its container (if it has one), for example, a class method and the class itself.

On the other hand, a Decorator can be applied to multiple components and can receive parameters , so it is possible to change its behavior depending on the component on which it is used.

Where can Decorators be applied?

A simple example of applying Decorators could be using them to log when class methods are executed.

To do this, as can be seen in the following image, a pair of classes with methods are declared, which are annotated with the “@log” tag to indicate that these methods must be decorated by applying the “log” decorator.

inspiring

The log decorator is then declared:

inspiring

This method takes as parameters the original method and a context object , which contains various data about the context in which the decorator method is executed (in this case, the name of the original method is used to display it). The method returns a function that executes statements before and after the call to the original method.
 

Finally, if we run the functions annotated with “@log” as follows: 
 

inspiring

We obtain the following result:

inspiring

On the other hand, decorators could be used to validate data from a class's properties/fields. However, since data initialization (init/constructor) and assignment (set) are performed separately, it would be necessary to define a decorator for each of these functions . To simplify this type of implementation, auto-accessors will be our best allies.

But... what are auto-accessors?

Auto-accessors are a new feature of the TypeScript language , added in version 4.9. They are similar to class fields, but differ in their runtime implementation. Due to the difference in runtime, it is possible to easily decorate both data initialization and assignment and retrieval.

 

To illustrate this case, the following example is presented, where it is validated that the number provided is not less than 0:

inspiring

To begin, a class is defined with an auto-accessor instead of a traditional field. To do this, the keyword accessor is used before the field definition.

The auto-accessor is annotated with the tag “ @minimumNumber0 ” to apply the decorator. Finally, the decorator is defined as follows:

inspiring

As you can see, the decorator receives the original initializers, getters, and setters from the auto-accessor, defines the function with which the field values will be validated, and applies the function to the decorated initializers and setters. Since nothing is required in the getter, it simply returns the original callback.

If you now try to provide values to the “age” auto-accessor as follows:

inspiring

The following results are obtained:

inspiring

Finally, the decorator could be parameterized to receive the minimum value instead of setting it to 0. This would be achieved by implementing a Decorator Factory .

Decorator Factory

A Decorator Factor allows you to provide parameters to a Decorator to alter its behavior.

 

To parameterize the Decorator used in the previous example, it would be enough to wrap the decorator in a function that receives a parameter, as can be seen in the following image:

inspiring

Additionally, it would be necessary to add the minimum value as a parameter to the tag with which the Decorator is called:

inspiring

Conclusions

Decorators are tools that can help with code reuse and improve code clarity. They also allow for implementing features other than those described above, such as dependency injection and entity access control, so it can be seen that they are tools with powerful and diverse capabilities.

On the other hand, depending on the tasks for which they are intended to be used, the complexity of their implementation may increase , so in certain cases they may not be the best option.

Want to know more about Hunters?

Being a Hunter means accepting the challenge of testing new solutions that deliver differentiated results. Join the Hunters program and become part of a cross-functional group capable of generating and transferring knowledge.

Get ahead of the digital solutions that will drive our growth. Learn more about Hunters .