Interview question-TS (8): What are decorators? How to use them in TypeScript?

Interview question-TS (8): What are decorators? How to use them in TypeScript?

In TypeScript, a decorator ( Decorators) is a special type declaration used to enhance the functionality of your code. Decorators provide a way to annotate or modify code elements such as classes, methods, and attributes, so that we can extend, modify, or monitor the behavior of code through decorators. By using decorators, we can add new functions to the code without modifying the original code, improving the maintainability and flexibility of the code.

1. What is a decorator?

A decorator is a special type of declaration @prefixed with a symbol followed by an expression, usually a function. Decorators can be attached to code elements such as classes, methods, properties, etc., and annotate or modify these elements at runtime.

In TypeScript, decorators are used mainly in the following two ways:

  1. Class decorator: Decorates the declaration of a class.
  2. Method decorators, property decorators, parameter decorators: decorate methods, properties and parameters in a class.

2. How to define a decorator?

A decorator is essentially a function that takes three parameters:

  1. For class decorators, its argument is the class constructor.
  2. For method decorators, its parameters are the prototype object of the class, the method name, and the attribute descriptor of the method.
  3. For property decorators, its parameters are the prototype object of the class and the property name.
  4. For parameter decorators, its parameters are the prototype object of the class, the method name and the index of the parameter.

The decorator function can obtain or modify the information of the class, method, attribute or parameter according to these parameters, and realize the corresponding function.

Here is an example of a simple decorator to add a log on a class:

function logClass(target: Function) {
    
    
  console.log("Class logged: ", target);
}

@logClass
class MyClass {
    
    
  // ...
}

In the above example, we defined a decorator function logClassthat receives the constructor of the class targetas a parameter and outputs the constructor of the class to the console. Then we use decorators @logClassto decorate MyClassthe class.

3. How to use decorators in TypeScript?

To use decorators in TypeScript, you first need to tsconfig.jsonenable experimentalDecoratorsthe option in:

{
    
    
  "compilerOptions": {
    
    
    "experimentalDecorators": true
  }
}

Then, we can use decorators on classes, methods, properties and parameters.

class decorator

A class decorator is a decorator applied to a class declaration. It is called when the class is declared and receives the class's constructor as an argument. Class decorators are often used to modify or extend the behavior of a class.

Here is an example of a simple class decorator to add a static property:

function addStaticProperty(target: Function) {
    
    
  target.staticProperty = "This is a static property.";
}

@addStaticProperty
class MyClass {
    
    
  // ...
}

console.log(MyClass.staticProperty); // 输出:This is a static property.

In the above example, we defined a class decorator addStaticPropertywhich adds a static property on class declaration staticProperty. Then we use the decorator @addStaticPropertyto decorate MyClassthe class and output the value of the static property to the console.

method decorator

A method decorator is a decorator applied to a method of a class. It will be called when the method is declared, and receive the prototype object of the class, the method name and the property descriptor of the method as parameters. Method decorators are often used to modify or monitor the behavior of a method.

Here is an example of a simple method decorator that adds a log:

function logMethod(target: any, methodName: string, descriptor: PropertyDescriptor) {
    
    
  const originalMethod = descriptor.value;
  descriptor.value = function (...args: any[]) {
    
    
    console.log("Method logged: ", methodName);
    return originalMethod.apply(this, args);
  };
}

class MyClass {
    
    
  @logMethod
  greet(name: string) {
    
    
    return `Hello, ${
      
      name}!`;
  }
}

const myClass = new MyClass();
myClass.greet("John"); // 输出:Method logged: greet
                       // 输出:Hello, John!

In the above example, we defined a method decorator logMethodwhich adds a logging functionality to the method declaration. Then we use decorators @logMethodto decorate the methods MyClassof the class greet.

property decorator

Property decorators are decorators applied to class properties. It is called when a property is declared and receives the class's prototype object and the property name as parameters. Property decorators are often used to modify or monitor the behavior of properties.

Here is an example of a simple property decorator to add a log:

function logProperty(target: any, propertyName: string) {
    
    
  let value = target[propertyName];
  const getter = function () {
    
    
    console.log("Property logged: ", propertyName);
    return value;
  };
  const setter = function (newVal: any) {
    
    
    console.log("Property set: ", propertyName);
    value = newVal;
  };
  Object.defineProperty(target, propertyName, {
    
    
    get: getter,
    set: setter,
  });
}

class MyClass {
    
    
  @logProperty
  message: string = "Hello";
}

const myClass = new MyClass();
console.log(myClass.message); // 输出:Property logged: message
                              // 输出:Hello

myClass.message = "World";     // 输出:Property set: message
console.log(myClass.message); // 输出:Property logged: message
                              // 输出:World

In the above example, we defined a property decorator logPropertywhich adds a logging function to the property declaration. Then we use decorators @logPropertyto decorate the properties MyClassof the class message.

parameter decorator

Parameter decorators are decorators applied to class method parameters. It will be called when the method parameters are declared, and receive the prototype object of the class, the method name and the index of the parameter as parameters. Parameter decorators are often used to modify or monitor the behavior of method parameters.

Here is an example of a simple parameter decorator to add a log:

function logParameter(target: any, methodName: string, parameterIndex: number) {
    
    
  console.log("Parameter logged: ", methodName, parameterIndex);
}

class MyClass {
    
    
  greet(@logParameter name: string) {
    
    
    return `Hello, ${
      
      name}!`;
  }
}

const myClass = new MyClass();
myClass.greet("John"); // 输出:Parameter logged: greet 0
                       // 输出:Hello, John!

In the above example, we defined a parameter decorator logParameterthat adds a logging function to the method parameter declaration. Then we use decorators @logParameterto decorate the parameters of the methods of MyClassthe class .greetname

Fourth, the combination of decorators

In TypeScript, we can combine multiple decorators together. The order of decorator composition is performed from top to bottom, that is, from outside to inside.

Here is an example of decorator composition:

function logClass(target: Function) {
    
    
  console.log("Class logged: ", target);
}

function addStaticProperty(target: Function) {
    
    
  target.staticProperty = "This is a static property.";
}

@logClass
@addStaticProperty
class MyClass {
    
    
  // ...
}

console.log(MyClass.staticProperty); // 输出:This is a static property.

In the above example, we first @addStaticPropertydecorate MyClassthe class with a decorator and then @logClassdecorate it with a decorator. @addStaticPropertySince the order of decorator combination is executed from outside to inside, the decorator is executed first , and then @logClassthe decorator is executed.

Five, custom decorator

In addition to using existing decorators, we can also customize decorators to achieve specific functions.

Here is an example of a simple custom decorator to time the execution of a method:

function logExecutionTime(target: any, methodName: string, descriptor: PropertyDescriptor) {
    
    
  const originalMethod = descriptor.value;
  descriptor.value = function (...args: any[]) {
    
    
    console.time(methodName);
    const result = originalMethod.apply(this, args);
    console.timeEnd(methodName);
    return result;
  };
}

class MyClass {
    
    
  @logExecutionTime
  longRunningTask() {
    
    
    // 模拟耗时任务
    for (let i = 0; i < 1000000000; i++) {
    
    }
  }
}

const myClass = new MyClass();
myClass.longRunningTask(); // 输出:longRunningTask: 2804.869ms

In the example above, we defined a custom decorator logExecutionTimethat adds timing functionality before and after method execution. Then we use decorators @logExecutionTimeto decorate the methods MyClassof the class longRunningTask.

6. Decorator factory

A decorator factory is a function that returns a decorator. It can take parameters and return a decorator function.

Here is an example of a simple decorator factory for adding a specified prefix:

function addPrefix(prefix: string) {
    
    
  return function (target: any, propertyName: string) {
    
    
    const value = target[propertyName];
    Object.defineProperty(target, propertyName, {
    
    
      get: function () {
    
    
        return prefix + value;
      },
      set: function (newVal: any) {
    
    
        value = newVal;
      },
    });
  };
}

class MyClass {
    
    
  @addPrefix("Hello, ")
  message: string = "World";
}

const myClass = new MyClass();
console.log(myClass.message); // 输出:Hello, World

In the above example, we defined a decorator factory addPrefixthat returns a decorator function for prepending the value of a property with the specified prefix. Then we use decorators @addPrefix("Hello, ")to decorate the properties MyClassof the class message.

7. Application scenarios of decorators

Decorators have many application scenarios in code. Here are some common use cases:

  1. Logging: Add a logging function to a method or class to record the execution process and results of the method.

  2. Performance monitoring: Add a performance monitoring function to a method or class to calculate the execution time of the method.

  3. Permission verification: Add a permission verification function to a method or class to check whether the user has the right to perform an operation.

  4. Data verification: add data verification function to the method or class to check whether the input data is legal.

  5. Cache processing: Add a cache processing function to a method or class to cache the results of the method.

Summarize

In TypeScript, a decorator is a special type declaration used to enhance the functionality of your code. Decorators provide a way to annotate or modify code elements such as classes, methods, and attributes, so that we can extend, modify, or monitor the behavior of code through decorators. By using decorators, we can add new functions to the code without modifying the original code, improving the maintainability and flexibility of the code. There are several types of decorators, such as class decorators, method decorators, attribute decorators, and parameter decorators. Each type has different usage scenarios and application methods. Let's take full advantage of decorators to improve the functionality and readability of our code and build more robust and maintainable applications.

Guess you like

Origin blog.csdn.net/weixin_42560424/article/details/131921655