TypeScript は、JavaScript 上に構築された厳密に型指定されたプログラミング言語であり、より優れたツールを大規模に提供します。 TypeScript は、JavaScript を使用してコードを作成するときに発生する問題のいくつかを解決するために開発されました。 TypeScript は、型を使用することで JavaScript の落とし穴を克服します。
TypeScript ソース コード内の各値には型があります。 TypeScript は、各値がその型に関連付けられたルールに準拠していることを確認します。 TypeScript は、プログラムを実行したり実行したりしなくても、ソース コード内のエラーをチェックします。
これは静的型チェックと呼ばれ、プログラムで使用される値の型に基づいて開発中のエラーをチェックします。
TypeScript は、より明確で読みやすいコードの作成を支援し、静的型チェックを提供するだけでなく、コードの可読性、再利用性、保守性を向上させる追加機能を提供します。そのような機能の 1 つが TypeScript デコレーターです。
TypeScript デコレータ
TypeScript のデコレーターは、実行時のコードの動作を変更または強化したり、コードにメタデータを追加したりできる機能です。デコレーターを使用すると、TypeScript でのメタプログラミングが可能になります。メタプログラミングは、プログラムが他のプログラムをデータとして扱い、その動作を変更できるプログラミング手法です。
基本的に、デコレータは、装飾された要素がアクセスまたは変更されるときに実行時にいくつかのロジックを実行するために呼び出される関数です。
このようにして、装飾された要素に追加の機能を追加できます。 TypeScript デコレーターは、クラス宣言、メソッド、プロパティ、アクセサー (ゲッターとセッター)、およびメソッド パラメーターにアタッチできます。
TypeScript では、デコレータには @ 記号が接頭辞として付け られ 、実行時に呼び出される関数を評価する式を含む @expression の形式をとります。 TypeScript でデコレータを使用するための一般的な構文は次のとおりです。
@decoratorName
itemToDecorate
単純なクラス デコレータの例を以下に示します。
function logClass(target: Function) {
console.log("The Log Class Decorator has been called")
console.log("Class:", target);
}
@logClass // @logClass is a decorator
class MyClass {
constructor() {
console.log("An instance of MyClass has been created");
}
}
const myInstance = new MyClass();
上記のコードを実行した結果は以下のようになります。
出力:
The Log Class Decorator has been called
Class: [class MyClass]
An instance of MyClass has been created
関数 logClass() は、 Function 型の target と呼ばれる単一の引数を受け取ります。引数 target は、装飾しているクラスのコンストラクターを受け取るため、 Function 型 になります。
logClass() を デコレータとして使用して MyClass というクラスを装飾するには、MyClass の宣言の直前に @logclass を置きます。デコレーターは、要素を装飾するために使用する関数と同じ名前を持つ必要があります。
MyClass のインスタンスが作成されると、出力に示されているように、クラス コンストラクターに加えてデコレーターの動作が実行されます。
デコレータは現在、実験的な機能として Typescript で利用できます。したがって、TypeScript でデコレーターを使用するには、tsconfig.json ファイルのコンパイラー オプションで ExperimentalDecorators を有効にする必要があります。
これを行うには、プロジェクト ディレクトリで開かれたターミナルで次のコマンドを実行して、TypeScript プロジェクト フォルダーに tsconfig.json ファイルを生成します。
tsc --init
tsconfig.json ファイルを取得したら、それを開いて、以下に示すように ExperimentalDecorators のコメントを解除します。
さらに、JavaScript のターゲット バージョンを少なくとも ES2015 に設定します。
TypeScript デコレータの重要性
優れたコードは、コードがどれだけ読みやすく、再利用しやすく、保守しやすいかによって簡単に判断できます。可読コードとは、理解しやすく解釈しやすく、コードを読む人に開発者の意図を明確に伝えるコードです。
一方、再利用可能なコードは、関数やクラスなどの特定のコンポーネントを、大幅な変更を加えることなく、アプリケーションの他の部分またはまったく新しいアプリケーションで再利用、適応、使用できるようにするコードです。
保守可能なコードとは、その存続期間中に簡単に変更、更新、修正できるコードです。
TypeScript デコレーターを使用すると、コードの可読性、再利用性、保守性を実現できます。まず、TypeScript デコレーターを使用すると、読みやすい宣言構文を使用してコードの動作を強化できます。デコレータでロジックをカプセル化し、ロジックが必要なコードのさまざまな要素を装飾することでデコレータを呼び出すことができます。
これにより、コードが読みやすく、何が起こっているかを理解しやすくなります。デコレータは開発者の意図を明確に伝えます。
デコレータは使い捨ての要素ではありません。本質的に再利用可能です。デコレータを一度作成すると、複数の領域で複数回使用できます。
したがって、デコレータを定義してインポートし、コードの動作を変更したいコード ベース内の任意の場所で使用できます。これにより、コード ベース内のロジックの重複を回避できるため、コードの再利用性が向上し、有益です。
また、デコレーターを使用すると、コードに大きな柔軟性とモジュール性が与えられ、さまざまな機能を独立したコンポーネントに分離できます。これは、読み取り可能で再利用可能なコードを記述できるという事実と合わせて、TypeScript デコレーターを使用すると、保守が容易なコードを作成できることを意味します。
TypeScript デコレータの種類
前述したように、TypeScript デコレーターは、クラス、クラス プロパティ、クラス メソッド、クラス アクセサー、およびクラス メソッド パラメーターにアタッチできます。装飾できる要素から、さまざまな種類の TypeScript デコレーターを取得します。これらのデコレータには次のものが含まれます。
#1. クラスデコレーター
クラス デコレータは、クラス定義を監視、変更、または置換するために使用されるデコレータです。これは、装飾しているクラスの直前に宣言されます。クラス デコレータは、デコレータが装飾しているクラスのコンストラクタに適用されます。実行時に、クラス デコレーターは、装飾しているクラスのコンストラクターを唯一の引数として呼び出します。
クラスの拡張を防ぐために使用されるクラス デコレータを以下に示します。
function frozen(target: Function) {
Object.freeze(target);
Object.freeze(target.prototype)
}
@frozen
class Vehicle {
wheels: number = 4;
constructor() {
console.log("A vehicle has been created")
}
}
class Car extends Vehicle {
constructor() {
super();
console.log("A car has been created");
}
}
console.log(Object.isFrozen(Vehicle));
クラスが拡張されないようにするには、関数 Object.freeze() を使用して、フリーズするクラスを渡します。デコレータは、この機能をクラスに追加するために使用されます。 Vehicle クラスを isFrozen() に渡すことで、実行時に Vehicle クラスがフリーズされているかどうかを確認できます。コードの出力を以下に示します。
true
#2. プロパティデコレーター
プロパティ デコレータはクラス プロパティを装飾するために使用され、プロパティ デコレータの直前に宣言されます。プロパティ デコレータを使用して、プロパティ定義を変更または監視できます。
実行時にデコレーターが呼び出され、2 つの引数を受け取ります。まず、メンバーが静的である場合はクラスのコンストラクター関数、インスタンス メンバーである場合はクラスのプロトタイプです。 2 番目の引数はメンバーの名前、つまり装飾するプロパティです。
TypeScript では、静的メンバーの前にキーワード static が付きます。静的メンバーには、クラスをインスタンス化せずにアクセスできます。インスタンス メンバーにはその前にキーワード static がなく、クラスのインスタンスが作成された後にのみアクセスできます。
プロパティ デコレータの例を以下に示します。
function wheelsDecorator(target: any, propertyName: string) {
console.log(propertyName.toUpperCase())
}
class Vehicle {
@wheelsDecorator
wheels: number = 4;
constructor() {
console.log("A vehicle has been created")
}
}
コードを実行した結果の出力を以下に示します。
WHEELS
#3. メソッドデコレータ
メソッド宣言の直前に宣言されたメソッド デコレータは、メソッド定義を監視、変更、または置換するために使用されるデコレータです。これは 3 つの引数を受け取ります。メンバーが静的である場合はクラスのコンストラクター関数、インスタンス メンバーである場合はクラスのプロパティです。
2 番目の引数はメンバーの名前で、最後にメンバーのプロパティ記述子です。プロパティ記述子は、オブジェクトのプロパティに関連付けられたオブジェクトであり、プロパティの属性と動作に関する情報を提供します。
メソッド デコレータは、メソッドの呼び出し前または呼び出し後に何らかのアクションを実行する場合に便利です。これらを使用して、呼び出されるメソッドに関する情報をログに記録することもできます。これは、メソッドが非推奨であることをユーザーに通知する場合に役立ちます。つまり、引き続き使用できますが、後で削除される可能性があるため、使用することはお勧めできません。
メソッド デコレータの例を以下に示します。
const logDeprecated =(target: any, methodName: string, descriptor: PropertyDescriptor) => {
console.log(`${methodName} has been deprecated`)
console.log(descriptor);
}
class Vehicle {
wheels: number = 4;
constructor() {
console.log("A vehicle has been created")
}
@logDeprecated
reFuel(): void {
console.log("Your vehicle is being refuelled");
}
}
出力:
reFuel has been deprecated
{
value: [Function: reFuel],
writable: true,
enumerable: false,
configurable: true
}
#4. アクセサ デコレータ
TypeScript には、get と set という 2 種類のアクセサー メソッドがあります。アクセサ メソッドは、クラス プロパティへのアクセスを制御するために使用されます。アクセサー デコレーターは、これら 2 つのアクセサー メソッドを修飾するために使用され、アクセサー宣言の直前に宣言されます。アクセサーは依然としてメソッドであるため、アクセサー デコレーターはメソッド デコレーターと同様に機能します。
アクセサ デコレータの例を以下に示します。
const logWheels =(target: any, accessorName: string, descriptor: PropertyDescriptor) => {
console.log(`${accessorName} used to get the number of wheels`)
console.log(descriptor);
}
class Vehicle {
private wheels: number = 4;
constructor() {
console.log("A vehicle has been created")
}
@logWheels
get numWheels(): number {
return this.wheels;
}
}
出力:
numWheels used to get the number of wheels
{
get: [Function: get numWheels],
set: undefined,
enumerable: false,
configurable: true
}
アクセサー デコレーターでは、デコレーターを同じ名前の複数の get/set アクセサーに適用できないことに注意することが重要です。たとえば、上記のコード サンプルでは、set numWheels というセッターを作成した場合、それに logWheels デコレータを使用することはできません。
#5. パラメータデコレータ
パラメーター デコレーターは、パラメーターがメソッドで宣言されていることを確認するために使用され、パラメーター宣言の前に宣言されます。パラメーター デコレーターは 3 つの引数を受け取ります。1 つは、静的メンバーのクラスのコンストラクター関数、またはインスタンス メンバーのクラスのプロトタイプです。
2 番目の引数はメンバーの名前、つまりパラメーター名です。 3 番目の引数は、関数のパラメータ リスト内のパラメータの序数インデックスです。つまり、最初のパラメーターがインデックス 0 にある場合、パラメーターはパラメーター リスト内でどの位置にありますか?
パラメータ デコレータの例を以下に示します。
const passengerLog = (target: Object, propertyKey: string, parameterIndex: number) => {
console.log(`Decorator on ${propertyKey}'s paremeter index ${parameterIndex}`);
}
class Vehicle {
private wheels: number = 4;
constructor() {
console.log("A vehicle has been created")
}
pickPassenger( location: string, numPassengers: string, @passengerLog driver: string) {
console.log(`${numPassengers} picked at ${location} by ${driver}`)
}
dropPassenger(driver: string, @passengerLog location: string, numPassengers: string) {
console.log(`${numPassengers} dropped at ${location} by ${driver}`)
}
}
出力:
Decorator on pickPassenger's paremeter index 2
Decorator on dropPassenger's paremeter index 1
結論
TypeScript デコレータは、デコレータを一度宣言すれば何度でも使用できるため、コードの可読性を高め、モジュール化された再利用可能なコードの作成に大いに役立ちます。さらに、デコレータはコード全体の保守性にも貢献します。
デコレータはまだ実験的な機能ですが、非常に便利なので、よく理解しておくことを検討してください。
TypeScript で文字列を数値に変換する方法もお読みください。