TypeScript - 装饰器



TypeScript 中的装饰器是一种特殊的声明,可以附加到类声明、属性、访问器、方法和参数上。它用于在不修改原始源代码的情况下单独修改类。这使得它们成为面向对象编程领域中强大的工具,允许您编写更简洁、更有条理的代码,并遵循 DRY(不要重复自己)原则。

在 TypeScript 中使用装饰器

您需要启用“experimentalDecorators”编译器选项以启用 TypeScript 项目中对装饰器的实验性支持。

有两种方法可以在 TypeScript 中启用“experimentalDecorators”编译器选项。您可以使用任何一个选项。

  • 在项目目录的终端中执行以下命令。tsc --target ES5 --experimentalDecorators
  • 或者,您可以更新tsconfig.js文件并在 compilerOptions 对象中添加"experimentalDecorators": true 属性。
    {
        "compilerOptions": {
            "target": "ES5",
            "experimentalDecorators": true
        }
    }
    

装饰器语法

您可以按照以下语法在 TypeScript 中使用装饰器。

@DecoratorName

在上述语法中,“DecoratorName”是带有“@”符号前缀的函数名。表达式必须在运行时计算“DecorateName”函数。

装饰器工厂

每当您需要自定义装饰器函数如何应用于声明时,它们可以使用装饰器工厂。装饰器工厂函数返回将在运行时计算的表达式。

请按照以下语法使用装饰器工厂函数。

function decoratorName(args: string) {
  // Decorator factory returns the function expression
  return function (target) {
    // This is the decorator which will be evaluated at the run time.
  };
}

在上述语法中,“args”是传递给装饰器函数的参数,“target”是类的原型。

装饰器组合

您可以将多个装饰器与特定声明一起使用。多个装饰器可以单行使用或多行使用,如下所示。

单行

@f @g x

或者,多行

@f
@g
x

在上述语法中,“f”和“g”装饰器与单个声明“x”一起使用。

为什么要使用装饰器?

让我们举一个简单的例子来了解装饰器的用例。

// Defining a class
class Student {
    // Declaring the properties of the class
    constructor(private name: string, private rollNo: number) { }

    // Defining the methods of the class
    sayHello() {
        console.log(`Hello, my name is ${this.name}.`);
    }

    printrollNo() {
        console.log(`My RollNo is ${this.rollNo}.`);
    }
}

// Creating an object of the class
const user = new Student("John", 20);
// Accessing the properties of the class
user.sayHello();
user.printrollNo();

编译后,它将生成以下 JavaScript 代码

// Defining a class
class Student {
    // Declaring the properties of the class
    constructor(name, rollNo) {
        this.name = name;
        this.rollNo = rollNo;
    }
    // Defining the methods of the class
    sayHello() {
        console.log(`Hello, my name is ${this.name}.`);
    }
    printrollNo() {
        console.log(`My RollNo is ${this.rollNo}.`);
    }
}

// Creating an object of the class
const user = new Student("John", 20);
// Accessing the properties of the class
user.sayHello();
user.printrollNo();

它产生以下输出

Hello, my name is John.
My RollNo is 20.

现在,如果我们希望在函数执行开始和结束时记录该函数,该怎么办?我们需要在每个函数的开头和结尾添加日志,如下面的代码所示。

// Defining a class
class Student {
    // Declaring the properties of the class
    constructor(private name: string, private rollNo: number) { }

    // Defining the methods of the class
    sayHello() {
        console.log("Start: sayHello");
        console.log(`Hello, my name is ${this.name}.`);
        console.log("End: sayHello");
    }

    printrollNo() {
        console.log("Start: printrollNo");
        console.log(`My RollNo is ${this.rollNo}.`);
        console.log("End: printrollNo");
    }
}

// Creating an object of the class
const user = new Student("John", 20);
// Accessing the properties of the class
user.sayHello();
user.printrollNo();

编译后,它将生成以下 JavaScript 代码

// Defining a class
class Student {
    // Declaring the properties of the class
    constructor(name, rollNo) {
        this.name = name;
        this.rollNo = rollNo;
    }
    // Defining the methods of the class
    sayHello() {
        console.log("Start: sayHello");
        console.log(`Hello, my name is ${this.name}.`);
        console.log("End: sayHello");
    }
    printrollNo() {
        console.log("Start: printrollNo");
        console.log(`My RollNo is ${this.rollNo}.`);
        console.log("End: printrollNo");
    }
}
// Creating an object of the class
const user = new Student("John", 20);
// Accessing the properties of the class
user.sayHello();
user.printrollNo();

它将产生以下输出

Start: sayHello
Hello, my name is John.
End: sayHello
Start: printrollNo
My RollNo is 20.
End: printrollNo

如果我们想重用记录函数执行的逻辑而不编写重复的代码,该怎么办?在这里,装饰器就派上用场了。

让我们通过下面的例子来学习它。

// Decorator factory
function printExecution(method: any, _context: any) {
    // Returning a new function
    return function (value: any, ...args: any[]) {
        // Logging the method name at the start
        console.log("start:", method.name);
        // Calling the original method
        const result = method.call(value, ...args);
        // Logging the method name at the end
        console.log("end:", method.name);
        return result;
    }
}

// Defining a class
class Student {
    // Declaring the properties of the class
    constructor(private name: string, private rollNo: number) { }

    // Defining the methods of the class
    @printExecution
    sayHello() {
        console.log(`Hello, my name is ${this.name}.`);
    }

    @printExecution
    printrollNo() {
        console.log(`My RollNo is ${this.rollNo}.`);
    }
}

// Creating an object of the class
const user = new Student("John", 20);
// Accessing the properties of the class
user.sayHello();
user.printrollNo();

以上代码打印与之前代码相同的输出。

Start: sayHello
Hello, my name is John.
End: sayHello
Start: printrollNo
My RollNo is 20.
End: printrollNo

类装饰器

类装饰器与类声明一起使用,以观察或修改类定义。

示例

// Decorator factory
function LogClass(target: Function) {
    console.log(`${target.name} is instantiated`);
}

// Decorator
@LogClass
class MyClass {
    constructor() { console.log("MyClass instance created"); }
}

// Create an instance of the class
const myClassInstance = new MyClass();

编译后,它将生成以下 JavaScript 代码

// Class definition
class MyClass {
    constructor() { console.log("MyClass instance created"); }
}
// Decorator
LogClass(MyClass);
// Create an instance of the class
const myClassInstance = new MyClass();

输出

MyClass instance created
MyClass is instantiated

方法装饰器

方法装饰器用于替换、修改或观察方法定义。它与方法定义一起使用。

示例

// Decorator factory
function printExecution(method: any, _context: any) {
    // Returning a new function
    return function (value: any, ...args: any[]) {
        // Logging the method name at the start
        console.log("start:", method.name);
        // Calling the original method
        const result = method.call(value, ...args);
        return result;
    }
}

// Defining a class
class Person {
    constructor(private name: string) { }

    // Decorator
    @printExecution
    printName() {
        console.log(`Hello, my name is ${this.name}.`);
    }
}

// Create an object of the class
const user = new Person("John");
// Accessing the properties of the class
user.printName();

输出

start: printName
Hello, my name is John.

访问器装饰器

访问器装饰器与 get() 和 set() 访问器一起使用,以观察、替换和修改访问器的定义。

示例

// Decorator factory
function LogAccessor(method: any) {
    console.log("Getter called");
}

// Define a class
class MyClass {
    private _name: string = "MyClass";

    // Decorator
    @LogAccessor
    get name() {
        return this._name;
    }
}

const instance = new MyClass();
console.log(instance.name);

输出

Getter called
MyClass

属性装饰器

属性装饰器与属性一起使用,以修改、替换和观察它。

示例

// Decorator function to log the property name
function LogProperty(target: any, propertyKey: string) {
    console.log(`Property declared: ${propertyKey}`);
}

class Person {
    // Decorator is applied to the property
    @LogProperty
    name: string;

    @LogProperty
    age: number;

    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
}

let person = new Person('Jay', 23);

输出

Property declared: name
Property declared: age

参数装饰器

参数装饰器与方法参数一起使用,以观察、修改和替换它们。

示例

// Decorator with parameter
function LogParameter(target: any, methodName: string, parameterIndex: number) {
    console.log(`Parameter in ${methodName} at index ${parameterIndex} has been decorated`);
}

// Class decorator
class MyClass {
    myMethod(@LogParameter param: string) {
        console.log(`Executing myMethod with param: ${param}`);
    }
}

// Create an instance of the class
const instance = new MyClass();
instance.myMethod("test");

输出

Parameter in myMethod at index 0 has been decorated
Executing myMethod with param: test

我们已经学习了如何在 TypeScript 中创建自定义装饰器。但是,用户可以将预定义的装饰器用于各种目的,例如调试代码等。

广告