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 中建立自定義裝飾器。但是,使用者可以將預定義的裝飾器用於各種目的,例如除錯程式碼等。

廣告