TypeScriptデコレータの簡単な紹介

デコレータをゼロから作成して、この強力なパターンをわかりやすく説明します

UnsplashのCederic Xによる写真

デコレータは、Typescriptが提供する最も強力な機能の1つであり、クリーンで宣言的な方法でクラスとメソッドの機能を拡張できます。デコレータは現在JavaScriptの第2段階の提案ですが、AngularやInversifyなどの主要なオープンソースプロジェクトで使用されているTypeScriptエコシステムで既に人気を集めています。

どんなに素晴らしいデコレーターであっても、彼らの行動を理解するのは気が遠くなるかもしれません。この記事では、5分以内にデコレータをしっかり理解することを目指しています。それでは、これらのデコレーターはどういうものですか?

デコレータは、コードを関数でラップするためのきれいな構文です

デコレータは実験的な機能であるため、実装の詳細は将来変更される可能性がありますが、基本的な原則は永遠です。デコレータの使用を許可するには、tsconfig.jsonファイルのコンパイラオプションに「experimentalDecorators」:trueを追加し、トランスピレーションターゲットがES5以降であることを確認します。

さて、時計は刻々と過ぎているので、コーディングしましょう!

旅はクラスデコレーターから始まります

古い城を強力な家族に貸し出すビジネスを営んでおり、HTTPサーバーのセットアップに取り組んでいるとします。 APIの各エンドポイントをクラスとして構築することにしました。クラスのパブリックメソッドはHTTPメソッドに対応します。これは次のようになります。

これはいいスタートであり、これらのクラスのそれぞれをHTTPサーバーのエンドポイントとして「登録」する簡単な方法が必要です。それを処理する簡単な関数を作成しましょう。この関数は引数としてクラスを取得し、そのクラスのインスタンスをエンドポイントとしてサーバーに追加します。そのようです:

気付かないうちに、私たちはすでに最初のデコレータを作成しました!そうです、それはそれと同じくらい簡単です。デコレータはすべて、クラスを引数として取る関数であり、ここにそれがあります。 「通常の」方法でregisterEndpointを呼び出す代わりに、クラスを@registerEndpointで装飾するだけです。信じられない?ご覧ください:

最初のデコレータを起動して実行したところ、2分ほどで完了しました。これで、メソッドデコレータの機能をさらに深く掘り下げて解き放つ準備が整いました。

メソッドデコレータの力を解き放つ

認証されたユーザーのみがエンドポイントにアクセスできるように、一部のエンドポイントを保護するとします。そのために、protectという新しいデコレーターを作成できます。今のところ、デコレータが行うことは、protectedMethodsという配列にprotectedメソッドを追加することだけです。

ご覧のとおり、メソッドデコレータは3つの引数を取ります。

  1. target —クラスのプロトタイプ(または、装飾されたメソッドが静的な場合はクラスのコンストラクター)。
  2. propertyKey —装飾されたメソッドの名前。
  3. 記述子—装飾された関数とそれに関するメタデータを保持するオブジェクト。

ここまでは、装飾したクラスとメソッドに関する情報のみを読みましたが、実際の楽しみはそれらの振る舞いを変え始めることから始まります。デコレータから値を返すだけでそれができます。メソッドデコレータが値を返す場合、元の記述子(元のメソッドを保持している)の代わりにこの値が使用されます。

nopeというデコレーターを作成して試してみましょう。これは、元のメソッドを、呼び出されるたびに「nope」を出力するメソッドに置き換えます。そのために、元の関数が格納されているdescriptor.valueを関数でオーバーライドします。

この時点で、メソッドデコレータの基本をいじりましたが、まだ有用なものは作成していません。次に保護デコレータを書き直す予定ですが、今回は装飾されたメソッドの名前を記録するだけでなく、実際に不正な要求をブロックします。

この次のステップは、最後のステップから少し複雑になるので、ご容赦ください。実行すべき手順は次のとおりです。

  1. 記述子から元の関数を抽出し、後で使用するために別の場所に保存します。
  2. descriptor.valueを、元の引数と同じ引数を取る新しい関数でオーバーライドします。
  3. 新しい関数内で、リクエストが認証されているかどうかを確認し、認証されていない場合はエラーをスローします。
  4. 最後に、新しい関数内でも、必要な引数を使用して元の関数を呼び出し、戻り値を取得して返すことができます。

すごい!完全に動作する保護デコレータがあります。メソッドの保護を追加または削除するのは簡単です。

8行目で元の関数をこれにバインドする方法に注意してください。これにより、クラスのインスタンスにアクセスできるようになります。たとえば、この行がないと、get()メソッドはthis.housesを読み取ることができません。

私たちはかなりの道を歩んできましたが、さらに先に進んでいます。この時点で、少し時間をとって、これまでに行ったことのすべてを理解しておくことをお勧めします。簡単にするために、上記のすべてのコードをここのプレイグラウンドに置いて、実行、変更、完全に理解できるまでブレークできるようにします。

デコレーターファクトリーでより多くのパワーを獲得

パス/ families / stark / membersで、Starkファミリーメンバーを返す新しいエンドポイントを追加したいとします。まあ、明らかにその名前でクラスを作成することはできませんので、私たちはどうしますか?

ここで必要なのは、デコレーター関数の動作を指示するパラメーターを渡す方法です。古き良きregisterEndpointデコレータをもう一度見てみましょう。

デコレータにパラメータを送信するには、通常のデコレータからデコレータファクトリに変換する必要があります。デコレータファクトリは、デコレータを返す関数です。これを作成するには、次のように元のデコレータをラップするだけです。

保護デコレータをラップすることもできるので、期待されるトークンをパラメータとして取得します。

結論

この時点で、デコレータ、それらがどのように機能するか、プログラマとして私たちを許可する力と表現力について十分に理解できました。この記事では、最も有用で一般的なテクニックを取り上げましたが、学ぶべきことはまだたくさんあります。 Typescriptのデコレータは、クラスとメソッドだけでなく、クラスのプロパティとメソッド引数にも適用できます。デコレータについて新しく習得した知識があれば、ドキュメントから簡単に理解できることを願っています。いつものように、この記事のすべてのコードをプレイグラウンドに置いているので、先に進んでプレイしてください!

いや、やった!

この記事を楽しんだら、気軽に拍手してください。そうすれば、より多くの人が見つけて楽しむことができます。私が書いたものをもっと見たい場合は、私をフォローしてください。ご質問がある場合は、コメントでお知らせください。

また、ジェネリックと拡張、またはクラス設計に関する他のTypeScriptの記事もご覧ください。