SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

ますます便利になるTypeScript! バージョン3からの変更点と総まとめ

TypeScriptに導入された新たな仕組みのデコレータ、その使い方と利便性とは?

ますます便利になるTypeScript! バージョン3からの変更点と総まとめ 第6回

  • X ポスト
  • このエントリーをはてなブックマークに追加

 その登場以来、順当にアップデートを重ねているTypeScriptに関して、バージョン3から5.2までの変更点をまとめて紹介するのが、本連載です。前回は、クラス構文に関する変更点を紹介しました。今回は、5.2までの変更点をまとめて紹介する最後の回であり、デコレータを紹介します。

  • X ポスト
  • このエントリーをはてなブックマークに追加

ECMAScriptに対応した新たなデコレータ

 本連載は、TypeScriptのバージョン3から5.2までのアップデート内容を、テーマごとにバージョン横断で紹介する連載です。今回でこの連載も一区切りとなり、5.2までの変更点をまとめる最後となります。その最後の回として、デコレータを紹介します。

デコレータとは

 デコレータというのは、リスト1の(1)のように、クラス中に記述された@から始まるコードのことです。

リスト1:デコレータの例
class SelfIntro {
  @logging  // (1)
  show() {
    console.log("こんにちは!");
  }
}

 このデコレータという仕組みは、TypeScriptのバージョン1.5という初期の頃から存在しています。これは、ES7に向けてECMAScriptで提案されていたものを先行でTypeScriptに導入された仕組みです。このECMAScriptで提案されていたデコレータが、ようやくステージ3になったのを機に、そのECMAScriptの仕様に従った新たな仕組みのデコレータとして、バージョン5.0でTypeScriptに組み込まれました。

デコレータの働き

 このデコレータの働きを端的にいうと、クラスのメンバに対して処理を付与できる仕組みです。例えば、リスト1の例でいうと、show()メソッドの本来の処理は、コンソールに「こんにちは!」と表示させることです。この本来の処理とは別に、何か処理を付与したい場合に、例えば、logging()という関数を定義しておき、(1)の@loggingのように、その関数名に@をつけた記述を行います。この仕組みにより、show()メソッドが実行されるたびに、logging()関数内の処理も実行されるようになります。

デコレータの簡易的な作り方

 概論はここまでにしておき、実際にデコレータの作り方を紹介していきます。デコレータはメソッドはもちろん、任意のクラスメンバ、場合によってはクラス定義そのものに対しても付与できますが、その定義方法は対象によってほとんど違いはありません。そのため、リスト1に記述したメソッドに付与するlogging()関数を題材に、デコレータの作り方の基礎を紹介していきます。

デコレータ関数の基本形

 loggingデコレータ関数の一番簡易なコード例は、リスト2のようなものとなります。

リスト2:loggingデコレータ関数の一番簡易なコード例
function logging(originalMethod: any, context: any) {  // (1)
  function replacementMethod(this: any, ...args: any[]) {  // (2)
    console.log("メソッドが実行されました。");  // (3)
    originalMethod();  // (4)
  }
  return replacementMethod;  // (5)
}

 デコレータ関数の基本形は、(1)のように関数名をデコレータ名そのものとし、その中にさらに関数を定義し、その関数をリターンするコードとします。リスト2では、(2)のreplacementMethod()関数が該当し、(5)のようにリターンしています。そして、このリターンする関数の中に、デコレータとして付与したい処理を記述します。

 その際、関数内で本来の処理、例えば、リスト1ではloggingデコレータが付与されたshow()メソッドそのものの処理を呼び出す必要があります。リスト2では、(3)の「メソッドが実行されました。」とコンソール表示するコードがデコレータとして付与された処理であり、(4)のコードがデコレータが付与されたメソッド本来の処理の呼び出しコードです。このデコレータが付与されたメソッド本来の処理は、デコレータ関数本体の第1引数として渡されます。リスト2の(1)ではoriginalMethodとしていますので、これを(4)のように関数として実行することで、デコレータが付与されたメソッド本来の処理が実行されます。

 結果、リスト1のSelfIntroをnewしてshow()メソッドを実行した場合、コンソールには次のように表示されます。

メソッドが実行されました。
こんにちは!

 これは、リスト2の(3)と(4)のように、「デコレータの処理→デコレータを付与したメソッド本来の処理」の順序でコードを記述したからです。この順序は、もちろん任意であり、リスト2の(3)と(4)のコードを入れ替えると、次のように実行結果も入れ替わります。

こんにちは!
メソッドが実行されました。

replacementMethod()関数定義の注意点

 なお、リスト2では、可読性を重視し、replacementMethod()関数を定義した上でリターンしていますが、次のように無名関数を利用してもかまいません。

function logging(originalMethod: any, context: any) {
  return function(this: any, ...args: any[]) {
    :
  }
}

 ただし、次のようにアロー関数は利用できないので、注意しましょう。というのは、後述するように、デコレータではthisを利用するのですが、このthisが実行時に決まらなければならないからです(アロー関数では定義時に決定します)。実際、TypeScriptでは、以下のコードはエラーとなります。

function logging(originalMethod: any, context: any) {
  return (this: any, ...args: any[]) => {
    :
  }
}

メソッドの引数

 リスト1のshow()には引数は定義されていませんでした。これが、もしshow(name: String)のように引数がある場合は、リスト2の(4)のoriginalMethodを実行する際に引数を渡す必要があります。そのための引数が、replacementMethod()関数の第2引数であり、デコレータを付与するメソッドが実行される際に渡された引数データがそのまま渡される仕組みとなっています。そこでこれを利用して、リスト3のようなコードを記述します。

リスト3:メソッドに引数が定義されている場合のデコレータ関数の例
function logging(originalMethod: any, context: any) {
  function replacementMethod(this: any, ...args: any[]) {
    console.log("メソッドが実行されました。");
    originalMethod(...args);
  }
  return replacementMethod;
}

 単に、replacementMethod()の第2引数であるargsを、originalMethod()の実行時に渡すだけです。このargsは、元々のメソッド引数の個数が何個か分かりませんので、可変長引数となっています。これをもとのメソッドに渡すために、スプレッド演算子を利用している点には注意してください。

メソッドの戻り値

 次に、デコレータを付与するメソッドに戻り値がある場合のコードサンプルを紹介するとします(リスト4)。

 なんのことはありません。originalMethodを実行した際に、デコレータを付与するメソッドの戻り値がそのまま返ってくるので、(1)のようにそれを受け取り(2)のようにreplacementMethod()関数の戻り値とするだけです。

リスト4:メソッドに戻り値がある場合のデコレータ関数の例
function logging(originalMethod: any, context: any) {
  function replacementMethod(this: any, ...args: any[]) {
    console.log("メソッドが実行されました。");
    const originalReturn = originalMethod(...args);  // (1)
    return originalReturn;  // (2)
  }
  return replacementMethod;
}

 もちろん、(1)と(2)の間に、コンソール出力などの処理を挟まないのならば、(1)と(2)は次のように1行で記述してもかまいません。

return originalMethod(...args);

会員登録無料すると、続きをお読みいただけます

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

次のページ
デコレータ関数の正しい作り方

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
ますます便利になるTypeScript! バージョン3からの変更点と総まとめ連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 齊藤 新三(サイトウ シンゾウ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook <個人紹介>WINGSプロジェクト所属のテクニカルライター。Web系製作会社のシステム部門、SI会社を経てフリーランスとして独立。屋号はSarva(サルヴァ)。HAL大阪の非常勤講師を兼務。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/19626 2024/06/14 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング