TypeScript 的 Decorator 小記

前言

前幾天在重構 Discord bot 時,碰上了相同的架構下需要人工 import 大量 module 的問題。既然在架構相同的情況下,能不能有效的降低人工重複的操作?這個想法不禁在我腦海中徘徊。後來剛好找到了 discordx 這個 repo,發現他們利用 decorator 來解決類似的問題,受到他們的啟發,我便開始自己嘗試撰寫 decorator 。

正文

前置作業

要開啟 TypeScript 的 decorator 功能,必須先在 tsconfig.json 中加入以下內容

1
2
3
4
5
{
"compilerOptions": {
"experimentalDecorators": true
}
}

或是在編譯時加上 --experimentalDecorators 參數。

TypeScript 中的 decorator

Decorator 是一種特殊的函式,可以用來修飾類別、方法、屬性等。在 TypeScript 中,共有以下幾種:

  • Class Decorator
  • Method Decorator
  • Accessor Decorator
  • Property Decorator
  • Parameter Decorator

Decorator 會根據類型不同而有不同的參數。

Decorator Factories

由於 Decorator 函式的參數是綁死的,沒辦法變動,無法應付一般常態的需求,因此可以利用工廠函式來根據參數生成 Decorator。以官方文件的例子來說

1
2
3
4
5
6
7
8
function color(value: string) {
// this is the decorator factory, it sets up
// the returned decorator function
return function (target) {
// this is the decorator
// do something with 'target' and 'value'...
};
}

我們用工廠函式生成對應的 Decorator,這樣就可以利用自己的參數控制 Decorator 的行為了。

Decorator 的參數

關於各個 Decorator 的參數,這邊就不再多贅述了,可以直接參考官方文件,這個小章節只會對幾個參數的值做簡單解釋。

主要這次我有接觸的是 Method Decorator,所以下面就以 Method Decorator 為例。

這是 Method Decorator 的宣告

1
function (target: any, propertyKey: string, descriptor: PropertyDescriptor);

target除了是物件的實體外,上頭還會帶有constructorpropertyKey是 method 的名稱,descriptorPropertyDescriptor的物件,我們可以利用它來設定 property descriptor,不過實際上我並沒有使用過。

如果我們想讓別的程式經由 Decorator 的註冊來呼叫 method,要記得透過 target 還有 propertyKey 來呼叫。否則會因為失去物件的實體導致 this 之類的關鍵字失效。

具體來說會像下面這樣來呼叫函式

1
target[propertyKey](...args);

結語

今天是在深夜找事情做的時候寫紀錄,所以寫得很偷懶,內容不多,感謝不嫌棄還讀到最後的你。