裝飾器是TypeScript中一項強大的元編程特性,被Angular和Vue3等主流框架廣泛使用。今天我們將深入探討這一高級特性。
裝飾器基礎
裝飾器是一種特殊類型的聲明,可以附加到類聲明、方法、訪問器、屬性或參數上。裝飾器使用@expression
形式,其中expression
必須是一個函數。
類裝飾器
類裝飾器在類聲明之前聲明,應用于類構造函數,可以觀察、修改或替換類定義。
function sealed(constructor: Function) {Object.seal(constructor);Object.seal(constructor.prototype);
}@sealed
class Greeter {greeting: string;constructor(message: string) {this.greeting = message;}greet() {return "Hello, " + this.greeting;}
}
上面的@sealed
裝飾器會阻止在類或其原型上添加或刪除屬性。
裝飾器工廠
如果需要自定義裝飾器行為,可以使用裝飾器工廠:
function color(value: string) {return function (target: any) {// 用某種方式存儲元數據target.prototype.color = value;}
}@color("red")
class Car {// ...
}const myCar = new Car();
console.log((myCar as any).color); // 輸出: red
方法裝飾器
方法裝飾器聲明在一個方法的聲明之前,可以用于觀察、修改或替換方法定義。
function log(target: any, key: string, descriptor: PropertyDescriptor) {const original = descriptor.value;descriptor.value = function(...args: any[]) {console.log(`Calling ${key} with`, args);const result = original.apply(this, args);console.log(`Called ${key}, returned`, result);return result;};return descriptor;
}class Calculator {@logadd(a: number, b: number): number {return a + b;}
}const calc = new Calculator();
calc.add(2, 3); // 日志會記錄調用和返回
屬性裝飾器
function format(formatString: string) {return function (target: any, propertyKey: string): any {let value = target[propertyKey];const getter = () => {return `${formatString} ${value}`;};const setter = (newVal: string) => {value = newVal;};return {get: getter,set: setter,enumerable: true,configurable: true};}
}class Greeter {@format("Hello,")greeting: string;constructor(message: string) {this.greeting = message;}
}const greeter = new Greeter("World");
console.log(greeter.greeting); // 輸出: Hello, World
參數裝飾器
function validate(target: any, propertyKey: string, parameterIndex: number) {const validations = target.__validations__ || (target.__validations__ = {});const paramValidations = validations[propertyKey] || (validations[propertyKey] = []);paramValidations[parameterIndex] = { type: "number", min: 0, max: 100 };
}class Temperature {setValue(@validate value: number) {console.log(`Temperature set to ${value}`);}
}
混入(Mixins)
混入是一種通過組合簡單部分類來構建復雜類的模式:
// 輔助函數
type Constructor<T = {}> = new (...args: any[]) => T;function Timestamped<TBase extends Constructor>(Base: TBase) {return class extends Base {timestamp = Date.now();};
}function Activatable<TBase extends Constructor>(Base: TBase) {return class extends Base {isActivated = false;activate() {this.isActivated = true;}deactivate() {this.isActivated = false;}};
}// 使用混入
class User {name = '';
}const TimestampedActivatableUser = Timestamped(Activatable(User));const user = new TimestampedActivatableUser();
user.name = "John";
user.activate();
console.log(user.name); // John
console.log(user.timestamp); // 當前時間戳
console.log(user.isActivated); // true
實際應用場景
-
日志記錄:自動記錄方法調用和參數
-
性能監控:測量方法執行時間
-
驗證:自動驗證方法參數
-
依賴注入:如Angular中的@Injectable
-
ORM映射:如TypeORM中的@Entity
最佳實踐
-
裝飾器應該保持簡單和專注
-
避免在裝飾器中引入副作用
-
考慮使用裝飾器工廠來提高靈活性
-
為裝飾器提供清晰的文檔說明
-
注意裝飾器的執行順序:
-
參數裝飾器 → 方法/屬性裝飾器 → 類裝飾器
-
從下到上(對于同一類型的多個裝飾器)
-
裝飾器和混入為TypeScript提供了強大的元編程能力,合理使用可以大幅提高代碼的可維護性和可擴展性。