How to use TypeScript decorators
TypeScript decorators offer an efficient and straightforward way to add extra functionality to objects without altering the original code. They can be applied to classes, methods, properties, accessors, and parameters, leveraging annotations and metadata.
What are TypeScript decorators and what are they used for?
The principle of TypeScript decorators is not fundamentally new. Similar features can be found in other programming languages, such as attributes in C#, decorators in Python or annotations in Java. This is a way of extending the functionality of an object without changing the source code. TypeScript has also been working with this approach for some time. Although most browsers do not (yet) support TypeScript decorators, it is still worth trying out this approach and its possibilities. Since version 5.0, the use of decorators has been massively simplified once again.
TypeScript decorators are used to add annotations and additional metadata for TypeScript classes and elements. In addition to the classes, methods, properties, access methods and parameters can also be changed. The latter can be checked, and the values retrieved. This is also a major difference between TypeScript decorators and their equivalent for JavaScript.
What is the syntax for decorators?
By adding TypeScript decorators to an object, you are essentially invoking a function that runs without altering the source code. This enhances functionality while maintaining clean and organized code. The basic syntax is as follows:
@nameOfTheDecorator
typescriptYou can create this function with either two or three parameters. The syntax for the function with three parameters looks like this:
function decoratorFunction(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log(`Decorating ${propertyKey} of class ${target.constructor.name}`);
}
class MyClass {
@decoratorFunction
myMethod() {
console.log('Executing myMethod');
}
}
typescriptThe individual TypeScript decorators components are made up as follows:
target
: Refers to the object that the decorator is assigned to.propertyKey
: This is a string representing the name of the class where the decorator is applied, which could be methods or properties.descriptor
: Stores additional details about the object the decorator is applied to, such as properties likevalue
,writable
,enumerable
, orconfigurable
.
Here is the syntax of TypeScript decorators with two parameters:
function decoratorFunction(target: any) {
console.log(`Decorating ${target.name}`);
}
@decoratorFunction
class MyClass {
}
typescriptIn this instance, decorators in TypeScript were used on a class.
What are the various types of decorators?
We will explore different types of TypeScript decorators in detail, each with its own unique characteristics:
- Class decorators
- Method decorators
- Property decorators
- Accessor decorators
- Parameter decorators
How to use TypeScript decorators for classes
If you want to customize the properties of a class and change its constructor, methods or properties, you can do so with TypeScript decorators. You receive the constructor as the first parameter as soon as you “decorate” the class with a function. This is an example of code where we are working with a customer list. It has some private and some public properties:
class Customers {
private static userType: string = "Generic";
private _email: string;
public customerName: string;
public street: string = "";
public residence: string = "";
public country: string = "";
constructor(customerName: string, email: string) {
this.customerName = customerName;
this._email = email;
}
static get userType() {
return Customers.userType;
}
get email() {
return this._email;
}
set email(newEmail: string) {
this._email = newEmail;
}
address(): string {
return `${this.street}\n${this.residence}\n${this.country}`;
}
}
const p = new Customers("exampleCustomer", "name@example.com");
p.street = "325 Lafayette Rd";
p.residence = "New Jersey";
typescriptIn the next step, we will incorporate TypeScript decorators to enhance functionality without retroactively modifying the source code. For the “Customer” class, we will apply the @frozen
decorator, which prevents objects from being altered afterward. We will use @required
for certain properties to mandate explicit input. Additionally, @enumerable
will be used for enumerations, and @deprecated
will indicate outdated inputs. Our first task will be to define these decorators:
function frozen(constructor: Function) {
Object.freeze(constructor);
Object.freeze(constructor.prototype);
}
function required(target: any, propertyKey: string) {
// Logic for required decorator
}
function enumerable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.enumerable = value;
};
}
function deprecated(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.warn(`The method ${propertyKey} is deprecated.`);
}
typescriptAfter using TypeScript decorators, the final code looks like this:
@frozen
class Customers {
private static userType: string = "Generic";
@required
private _email: string;
@required
public customerName: string;
public street: string = "";
public residence: string = "";
public country: string = "";
constructor(customerName: string, email: string) {
this.customerName = customerName;
this._email = email;
}
@enumerable(false)
get userType() {
return Customers.userType;
}
get email() {
return this._email;
}
set email(newEmail: string) {
this._email = newEmail;
}
@deprecated
address(): string {
return `${this.street}\n${this.residence}\n${this.country}`;
}
}
const p = new Customers ("exampleCustomer", "name@example.com");
p.street = "325 Lafayette Rd";
p.residence = "New Jersey";
typescriptMethod TypeScript decorators
TypeScript decorators can also be used for methods. Exceptions are declaration files, overloading or the “declare” class. In the following example, we are going to use @enumerable
as a decorator for the getName
method in the “Person” class:
const enumerable = (value: boolean) => {
return (target: any, propertyKey: string, propertyDescriptor: PropertyDescriptor) => {
propertyDescriptor.enumerable = value;
}
}
class Person {
firstName: string = "Julia"
lastName: string = "Brown"
@enumerable(true)
getName () {
return `${this.firstName} ${this.lastName}`;
}
}
typescriptProperty TypeScript decorators
TypeScript decorators for class properties (property decorators) take two parameters, which are the class’s constructor function and the name of the property. In the example below, we use the decorator to display the name of a property (such as the customer name):
const printPropertyName = (target: any, propertyName: string) => {
console.log(propertyName);
};
class Customers {
@printPropertyName
name: string = "Julia";
}
typescriptAccessor TypeScript decorators
Accessor decorators work on a principle similar to Property decorators, but with one key difference: they include an additional third parameter. In this context, that parameter is the Property Descriptor for a customer. When you use an Accessor Decorator to set a value, it updates the Property Descriptor accordingly. In the following code, for example, the boolean value (true or false) of enumerable is modified. Here’s the starting point:
const enumerable = (value: boolean) => {
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
descriptor.enumerable = value;
}
}
typescriptHere’s how to use the decorator:
class Customers {
firstName: string = "Julia";
lastName: string = "Brown";
@enumerable(true)
get name() {
return `${this.firstName} ${this.lastName}`;
}
}
typescriptParameter TypeScript decorators
TypeScript Parameter decorators also receive three arguments: the class constructor function, the name of the method, and the index of the parameter. However, the parameter itself cannot be modified, meaning this decorator is limited to validation purposes. To access the parameter index, you can use the following code:
function print(target: Object, propertyKey: string, parameterIndex: number) {
console.log(`Decorating param ${parameterIndex} from ${propertyKey}`);
}
typescriptIf you then apply the Decorator parameter, this is the code:
class Example {
testMethod(param0: any, @print param1: any) {}
}
typescriptIdeal for static websites and apps alike: With Deploy Now from IONOS, you benefit from simple staging, a quick setup and perfectly coordinated workflows. Find the right plan to suit your needs!