【TypeScript】デコレータ活用方法を10の具体的なコード例で徹底解説

TypeScriptのデコレータのイラストとコードのサンプルTypeScript
この記事は約30分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればサンプルコードを活用して機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

TypeScriptは、JavaScriptのスーパーセットとして開発された静的型付け言語です。

この言語の魅力の1つが「デコレータ」という強力な機能です。

デコレータを使うことで、コードの拡張や再利用が非常に容易になります。

この記事では、デコレータの基本から高度な利用方法、そして実際のコード例まで、初心者から上級者までを対象に、徹底的に解説していきます。

●デコレータの基本とは

TypeScriptにおけるデコレータの基本を理解するためには、まずその定義と概念を把握することが不可欠です。

デコレータは、クラスやメソッド、アクセサーなどの既存のコードに対して注釈や新たな振る舞いを提供する声明的なパターンです。

この強力な機能を使いこなすことで、クリーンなコードを保ちつつも、追加の機能を柔軟に適用することが可能になります。

次に、このデコレータが具体的にどういった役割を果たし、コードベースに対してどのようなメリットをもたらすのかを、具体例を交えながら探求していきましょう。

○デコレータの定義

デコレータは、クラス、メソッド、プロパティ、アクセサ、パラメータに特別なメタデータを追加するための宣言的な方法を提供するものです。

具体的には、特定のシンボルやオブジェクトに対して追加の振る舞いや属性を追加することができる機能です。

○デコレータの役割とメリット

デコレータの主な役割は、既存のコードを変更せずにその動作を変更したり、新しい機能を追加したりすることです。

これにより、コードの再利用性や拡張性が向上します。

また、特定のパターンや機能を一元的に管理できるため、コードの整理や保守も容易になります。

●デコレータの具体的な使い方

TypeScriptでのデコレータの使い方については、さまざまな場面で応用できます。

ここでは、特によく使用される「クラスデコレータ」について、具体的なコード例を交えて詳しく解説していきます。

○サンプルコード1:クラスデコレータの基本形

クラスデコレータは、クラスの定義に対して動的な振る舞いやメタデータを追加するためのものです。

このコードでは、クラスに追加のプロパティを持たせるデコレータを作成します。

// クラスデコレータの定義
function ClassDecorator(target: Function) {
    // targetはデコレータが適用されるクラスのコンストラクタ関数です
    target.prototype.addedProperty = "新しく追加されたプロパティ";
}

@ClassDecorator
class SampleClass {
    // このクラスはデコレータで新しいプロパティを追加されます
}

このコードでは、ClassDecoratorという名前のクラスデコレータを定義しています。

そして、SampleClassというクラスにこのデコレータを適用しています。

デコレータは@マークを使用してクラスの直前に記述します。

このコードを実行すると、SampleClassのインスタンスがaddedPropertyという新しいプロパティを持つことになります。

このプロパティはデコレータによって動的に追加されたものです。

下記のコードは、上記のSampleClassを実際にインスタンス化し、新しく追加されたプロパティをコンソールに出力するものです。

const instance = new SampleClass();
console.log(instance.addedProperty);  // 出力: 新しく追加されたプロパティ

このようにして、クラスデコレータは、クラスの定義やインスタンス化の際に動的に追加の振る舞いやプロパティを付与することができます。

この仕組みを利用すれば、コードの再利用性や拡張性を高めることが可能となります。

○サンプルコード2:メソッドデコレータの活用

TypeScriptのデコレータにおける重要な機能として「メソッドデコレータ」が挙げられます。

ここでは、メソッドデコレータの基本的な使い方を取り上げ、その利点や応用例をサンプルコードを通して詳しく見ていきます。

メソッドデコレータは、クラスのメソッドに特定の振る舞いや属性を追加するためのもので、デコレータがメソッドに適用される際、それが属しているクラスの情報と合わせて処理されます。

具体的な利用シーンとしては、ログ出力やエラーハンドリング、メソッドの実行時間の計測などが考えられます。

このコードでは、メソッドの実行前後にログを出力するシンプルなメソッドデコレータを作成しています。

function LogExecution(target: Object, propertyKey: string, descriptor: PropertyDescriptor): void {
    const originalMethod = descriptor.value;

    descriptor.value = function(...args: any[]) {
        console.log(`メソッド${propertyKey}が実行される前のログ`);
        const result = originalMethod.apply(this, args);
        console.log(`メソッド${propertyKey}が実行された後のログ`);
        return result;
    };
}

class MyClass {
    @LogExecution
    myMethod(param: string): void {
        console.log(`myMethodが実行されました。パラメータ:${param}`);
    }
}

const obj = new MyClass();
obj.myMethod("テスト");

このコードを実行すると、myMethodメソッドが呼び出される前後に、指定したログがコンソールに出力されます。

具体的には、次のようなメッセージが表示されます。

メソッドmyMethodが実行される前のログ
myMethodが実行されました。パラメータ:テスト
メソッドmyMethodが実行された後のログ

この方法で、メソッドの実行前後での処理を簡単に追加することができます。

もちろん、より複雑な条件下でのログ出力や、エラーハンドリングなどもこのメソッドデコレータを利用して実装することが可能です。

また、メソッドデコレータは、単なるログ出力だけでなく、さまざまな処理を追加するためにも使用できます。

例として、メソッドの実行時間を計測するデコレータを考えてみましょう。

function MeasureTime(target: Object, propertyKey: string, descriptor: PropertyDescriptor): void {
    const originalMethod = descriptor.value;

    descriptor.value = function(...args: any[]) {
        const startTime = Date.now();
        const result = originalMethod.apply(this, args);
        const endTime = Date.now();
        console.log(`メソッド${propertyKey}の実行時間:${endTime - startTime}ミリ秒`);
        return result;
    };
}

class SampleClass {
    @MeasureTime
    someMethod(): void {
        // 何かの処理
    }
}

このコードでは、MeasureTimeデコレータを使って、someMethodメソッドの実行時間を計測してコンソールに出力しています。

このように、メソッドデコレータを活用することで、コードのリファクタリングや拡張が簡単に行えるようになります。

○サンプルコード3:アクセサデコレータの利用方法

デコレータはTypeScriptの非常に強力な機能の1つであり、メソッドやクラスだけでなく、アクセサ(getterやsetter)にも適用できます。

アクセサデコレータは、オブジェクトのプロパティへのアクセス方法をカスタマイズする際に役立ちます。

具体的に、アクセサデコレータを用いて、プロパティへのアクセスをロギングするサンプルコードを見てみましょう。

function logAccess(target: any, key: string, descriptor: PropertyDescriptor): PropertyDescriptor {
    // 元々のgetterを取得
    const originalGetter = descriptor.get;

    // getterをカスタマイズ
    descriptor.get = function () {
        console.log(`"${key}"がアクセスされました。`);
        return originalGetter.call(this);
    };

    return descriptor;
}

class User {
    private _name: string;

    constructor(name: string) {
        this._name = name;
    }

    @logAccess
    get name() {
        return this._name;
    }
}

const user = new User("Taro");
console.log(user.name);

このコードではlogAccessというアクセサデコレータを定義しています。

このデコレータは、対象のプロパティがgetterを通じてアクセスされた際に、コンソールにアクセス情報を出力する機能を持っています。

この機能により、Userクラスのnameプロパティへのアクセスを監視しています。

上記のコードを実行すると、Userクラスのインスタンスを生成し、nameプロパティをアクセスすることで、コンソールに"name"がアクセスされました。というメッセージが出力されます。

○サンプルコード4:プロパティデコレータの基本

TypeScriptでは、クラスのプロパティにもデコレータを適用することができます。

このデコレータを「プロパティデコレータ」と言います。

プロパティデコレータは、特定のプロパティの動作や初期値を変更したり、追加の機能を付与するといったことが可能です。

プロパティデコレータの基本的な使い方を紹介します。

// デコレータの定義
function readonly(target: any, propertyKey: string) {
    // プロパティデスクリプタを取得
    const descriptor = Object.getOwnPropertyDescriptor(target, propertyKey) || {};
    // 書き換え不可に設定
    descriptor.writable = false;
    Object.defineProperty(target, propertyKey, descriptor);
}

class User {
    @readonly
    public name: string;

    constructor(name: string) {
        this.name = name;
    }
}

const user = new User("Taro");
user.name = "Jiro"; // これはエラーになる

このコードでは、readonlyというプロパティデコレータを定義しています。

このデコレータは、指定されたプロパティを書き換え不可に設定します。

したがって、Userクラスのnameプロパティに@readonlyデコレータを適用した場合、このプロパティは後から書き換えることができません。

このコードを実行すると、user.name = "Jiro";の行でエラーが発生します。

nameプロパティが書き換え不可に設定されているため、新しい値を代入しようとするとエラーが投げられます。

●デコレータの応用例

TypeScriptのデコレータは、クラスやメソッド、プロパティなどの構成要素にメタデータや振る舞いを追加するための便利な機能です。

ここでは、デコレータの高度な活用方法を一つの具体的な例を交えて解説します。

その例として、ログ出力の自動化を取り上げます。

○サンプルコード5:ログ出力の自動化

アプリケーションの開発やデバッグ時に、特定のメソッドがいつどのように呼び出されたのかを知るために、ログを出力することは一般的です。

しかし、それぞれのメソッドにログ出力のコードを書くのは煩雑です。

この問題を解決するために、デコレータを利用してログ出力を自動化することができます。

下記のコードは、メソッドデコレータを用いて、メソッドが呼び出された際の情報を自動的にログ出力する例です。

function log(target: Object, propertyKey: string, descriptor: PropertyDescriptor): void {
    const originalMethod = descriptor.value;

    descriptor.value = function (...args: any[]) {
        // ログ出力
        console.log(`メソッド "${propertyKey}" が ${new Date().toISOString()} に呼び出されました。`);
        return originalMethod.apply(this, args);
    };
}

class SampleClass {
    @log
    someMethod(param: string): void {
        console.log(param);
    }
}

このコードでは、logというデコレータを使って、SampleClasssomeMethodというメソッドが呼び出されると、その情報をログとして出力します。

具体的には、呼び出されたメソッドの名前と呼び出し時刻をコンソールに表示します。

このコードを実行すると、someMethodメソッドを呼び出すたびに、次のようなログがコンソールに表示されます。

メソッド "someMethod" が 2023-08-29T10:00:00.000Z に呼び出されました。

このように、デコレータを活用することで、特定のメソッドの呼び出し情報を簡単にログとして取得することができます。

これにより、デバッグや監視の作業が大幅に効率化されるでしょう。

○サンプルコード6:メソッドの実行時間計測

TypeScriptのデコレータは非常に強力で、さまざまな場面で利用することができます。

今回は、デコレータを使ってメソッドの実行時間を計測する方法を紹介します。

これにより、アプリケーションのパフォーマンスチューニング時に、どのメソッドがどれくらいの時間を取っているのかを知るのに役立ちます。

メソッドの実行時間を計測するデコレータのサンプルコードを紹介します。

// 実行時間計測デコレータ
function measureTime(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) {
    const originalMethod = descriptor.value;

    descriptor.value = function(...args: any[]) {
        const start = performance.now();
        const result = originalMethod.apply(this, args);
        const end = performance.now();
        console.log(`${propertyKey}の実行時間: ${end - start}ms`);
        return result;
    }

    return descriptor;
}

このコードではmeasureTimeというデコレータを定義しています。

デコレータは、メソッドに適用されると、そのメソッドの実行前と実行後での時間をperformance.now()を使って取得します。

そして、その差分を計算して実行時間をconsole.logで出力します。

次に、このデコレータをどのように使用するかのサンプルを見てみましょう。

class SampleClass {
    @measureTime
    someMethod() {
        for (let i = 0; i < 1000000; i++) {}
    }
}

const sample = new SampleClass();
sample.someMethod();

上記のSampleClasssomeMethodに、先ほど定義したmeasureTimeデコレータを適用しています。

このメソッドは、単にループを1,000,000回回すだけのものですが、実行されるとその実行時間がコンソールに表示されます。

このサンプルコードを実行すると、例えば「someMethodの実行時間: 3.215ms」といったような出力が得られます。

この結果から、someMethodが約3.2ミリ秒かかって実行されたことがわかります。

ただし、実行結果は実行環境やその他の条件によって異なることがありますので、必ずしも同じ結果にはなりません。

○サンプルコード7:条件付きメソッドの実行

デコレータはTypeScriptの魅力的な機能の一つであり、様々な状況でその力を発揮します。

ここでは、特定の条件下でのみメソッドを実行するためのデコレータを解説します。

このような機能は、アクセス制御や特定の状況下でのみ処理を実行したい場合に非常に役立ちます。

例として、あるクラスのメソッドを、特定の条件が真の場合のみ実行するデコレータを作成します。

function ConditionalExecute(condition: () => boolean): MethodDecorator {
    return (target, propertyKey, descriptor) => {
        const originalMethod = descriptor.value;
        descriptor.value = function(...args: any[]) {
            if (condition()) {
                return originalMethod.apply(this, args);
            }
            console.log(`条件が満たされないため、${String(propertyKey)}は実行されませんでした。`);
        };
        return descriptor;
    };
}

class SampleClass {
    public value: number = 0;

    @ConditionalExecute(() => Math.random() > 0.5)
    increment() {
        this.value++;
        console.log(`現在のvalueは${this.value}です。`);
    }
}

このコードでは、ConditionalExecuteというデコレータを定義しています。

このデコレータは条件関数を受け取り、その関数がtrueを返す場合のみメソッドを実行します。

サンプルクラスSampleClassincrementメソッドは、乱数が0.5より大きい場合のみ実行されます。

このコードを実行すると、incrementメソッドを呼び出しても、条件が満たされない場合はメソッドが実行されず、代わりにメッセージがコンソールに表示されます。

逆に、条件が満たされる場合は、incrementメソッドが正常に実行され、valueプロパティがインクリメントされます。

const sample = new SampleClass();
sample.increment();  // この呼び出しは条件によって異なる結果が得られます。

条件関数がtrueを返す場合、結果として「現在のvalueは1です。」と表示されます。

しかし、条件関数がfalseを返す場合、代わりに「条件が満たされないため、incrementは実行されませんでした。」と表示されるでしょう。

○サンプルコード8:デコレータでのエラーハンドリング

TypeScriptのデコレータは、メタプログラムやアスペクト指向プログラミングのような概念を実現するための強力なツールとして知られています。

ここでは、エラーハンドリングの際のデコレータの活用法について、具体的なコード例とともに解説します。

従来、関数やメソッドの中で発生したエラーをハンドリングする際には、try-catch文を多用する必要がありました。

しかしこれにより、本来のビジネスロジックが複雑になることが少なくありません。

デコレータを活用することで、このようなエラーハンドリングのロジックを効率的に外部化することが可能となります。

エラーハンドリングをデコレータで実現するためのサンプルコードを紹介します。

// エラーハンドリング用のデコレータ
function catchError(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    // このコードでは、オリジナルのメソッドをラップしています。
    descriptor.value = function (...args: any[]) {
        try {
            return originalMethod.apply(this, args);
        } catch (error) {
            console.error(`エラーが発生しました: ${error.message}`);
        }
    };

    return descriptor;
}

class SampleClass {
    @catchError
    riskyMethod() {
        // このコードでは、意図的にエラーを発生させています。
        throw new Error("意図的なエラー");
    }
}

const sample = new SampleClass();
sample.riskyMethod();  // このコードを実行すると、コンソールにエラーメッセージが出力されます。

このコードでは、catchErrorというデコレータを定義しています。

このデコレータは、エラーが発生した場合にコンソールにエラーメッセージを出力するような機能を持っています。

そして、SampleClassの中にriskyMethodというメソッドを定義し、このメソッドに先ほどのデコレータを適用しています。

このメソッドを実行すると、意図的にエラーを発生させるコードが含まれているため、エラーハンドリングがデコレータによって行われ、コンソールにエラーメッセージが表示されます。

○サンプルコード9:メタデータの付与と取得

TypeScriptのデコレータを使用して、クラスやメソッドに追加的なメタデータを付与し、後でそのメタデータを取得する方法について解説します。

メタデータは、データを記述するためのデータです。

例えば、クラスやメソッドに関する情報を追加的に保存しておき、後からその情報を取得したい場合に使用します。

下記のサンプルコードは、Reflectというグローバルオブジェクトを使用して、クラスのメソッドにメタデータを付与し、後でそのメタデータを取得する例です。

// デコレータ関数の定義
function SetMetadata(key: string, value: any) {
  return function (target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
    Reflect.defineMetadata(key, value, target, propertyKey);
  };
}

class Sample {
  @SetMetadata("info", "これはサンプルのメソッドです")
  sampleMethod() {
    console.log("サンプルのメソッドが実行されました。");
  }
}

const instance = new Sample();
instance.sampleMethod();

const metadata = Reflect.getMetadata("info", instance, "sampleMethod");
console.log(`メタデータ情報: ${metadata}`);

このコードでは、SetMetadataというデコレータ関数を定義しています。

このデコレータは、指定されたキーと値のペアのメタデータをメソッドに付与します。

ここでReflect.defineMetadataを使用して、メタデータをメソッドに関連付けます。

また、SampleクラスのsampleMethodメソッドに@SetMetadataデコレータを適用して、”info”というキーでメタデータを付与しています。

最後に、Reflect.getMetadataを使用して、メタデータを取得し、その内容をコンソールに表示しています。

このコードを実行すると、まず”サンプルのメソッドが実行されました。”というメッセージが表示され、その後に”メタデータ情報: これはサンプルのメソッドです”というメッセージが表示されます。

これにより、メソッドに付与されたメタデータが正しく取得できることが確認できます。

○サンプルコード10:デコレータを組み合わせるテクニック

デコレータを組み合わせることで、より強力な機能を持つカスタムデコレータを作成することが可能です。

デコレータの組み合わせにより、各デコレータの持つ機能を統合して、一つのアクションとして表現することができます。

ここでは、複数のデコレータを組み合わせるテクニックについて、具体的なサンプルコードと共に徹底的に解説します。

例として、ログ出力デコレータとメソッドの実行時間計測デコレータを組み合わせることを考えます。

これにより、メソッドが呼び出されたときのログと、そのメソッドの実行時間を同時に出力するデコレータを作成します。

// ログ出力デコレータ
function log(target: any, propertyName: string, descriptor: PropertyDescriptor) {
    const method = descriptor.value;
    descriptor.value = function (...args: any[]) {
        console.log(`${propertyName}が呼び出されました。`);
        return method.apply(this, args);
    }
}

// メソッドの実行時間計測デコレータ
function measure(target: any, propertyName: string, descriptor: PropertyDescriptor) {
    const method = descriptor.value;
    descriptor.value = function (...args: any[]) {
        console.time(`${propertyName}の実行時間`);
        const result = method.apply(this, args);
        console.timeEnd(`${propertyName}の実行時間`);
        return result;
    }
}

class Sample {
    @log
    @measure
    greet() {
        console.log("Hello, TypeScript!");
    }
}

const sample = new Sample();
sample.greet();

このコードでは、logデコレータとmeasureデコレータを組み合わせています。

@logデコレータは、メソッドが呼び出されたときに、そのメソッド名とともにログを出力します。

一方、@measureデコレータは、メソッドの実行時間を計測して、それをログとして出力します。

このコードを実行すると、次のような結果を得られるでしょう。

greetが呼び出されました。
Hello, TypeScript!
greetの実行時間: 〇〇ms (具体的な時間は環境により異なります)

このように、複数のデコレータを組み合わせることで、それぞれのデコレータが持つ機能を統合し、一つのアクションとして表現することができます。

特に、デコレータを組み合わせる際には、適用するデコレータの順序に注意が必要です。

上記の例では、@logが先に適用され、その後に@measureが適用されています。

この順序を逆にすると、実行結果も変わることがありますので、注意が必要です。

●デコレータ使用時の注意点と対処法

TypeScriptのデコレータは、メタプログラミングの強力なツールとして、クラスやそのメンバーに対する動的な動作を追加するために使用されます。

しかし、その強力さゆえに、使用する際にはいくつかの注意点が必要です。

ここでは、デコレータを使用する際の一般的な問題や、それらを回避または修正する方法を具体的なサンプルコードとともに説明します。

○デコレータの適用順序

デコレータが複数存在する場合、その適用順序が重要となります。

デコレータは、下から上へと逆の順序で評価され、しかし実際の適用はその逆、つまり上から下へと行われます。

例えば、次のコードを考えてみましょう。

function Deco1() {
    return function(target) {
        console.log('Deco1適用');
    }
}

function Deco2() {
    return function(target) {
        console.log('Deco2適用');
    }
}

@Deco1()
@Deco2()
class SampleClass {}

このコードでは、「Deco2を使って」というメッセージが先に表示され、次に「Deco1を使って」というメッセージが表示されます。

この理解は、複数のデコレータを組み合わせて使用する際に非常に重要です。

○デコレータとリフレクション

TypeScriptでは、リフレクションを使ってデコレータが適用されたクラスやそのメンバーの情報を取得することができます。

しかし、これにはreflect-metadataというライブラリの使用が必要です。

下記のコードは、メタデータを使用してデコレータが適用されたメソッドの情報を取得する例です。

import "reflect-metadata";

function MyDecorator(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) {
    const method = descriptor.value;
    Reflect.defineMetadata("description", "これはサンプルのメソッドです", target, propertyKey);
}

class MyClass {
    @MyDecorator
    myMethod() {}
}

const description = Reflect.getMetadata("description", MyClass.prototype, "myMethod");
console.log(description); // これはサンプルのメソッドです

このコードでは、Reflect.defineMetadataを使ってメソッドに対してメタデータを設定しています。

そして、Reflect.getMetadataを使用してその情報を取得しています。

○デコレータのパフォーマンスへの影響

デコレータは実行時に動作するため、過度な使用はパフォーマンスに影響を及ぼす可能性があります。

特に、デコレータ内での重い処理や、大量のデコレータの使用は避けるべきです。

もしデコレータのパフォーマンスへの影響を懸念する場合、適切なツールを使用してプロファイリングを行い、実際の影響を確認することが推奨されます。

○デコレータと継承

デコレータがクラスに適用された場合、そのクラスを継承したサブクラスにもデコレータの効果が及ぶことがあります。

これは予期しない動作を引き起こす可能性があるため、注意が必要です。

例として、次のコードを考えてみましょう。

function MyDecorator(target) {
    console.log('デコレータ適用:', target.name);
}

@MyDecorator
class Parent {}

class Child extends Parent {}

このコードを実行すると、デコレータはParentクラスにのみ適用されているにも関わらず、「デコレータ適用: Parent」というメッセージが表示されます。

このような継承とデコレータの関係性を理解し、適切にデコレータを使用することが重要です。

●デコレータのカスタマイズ方法

TypeScriptのデコレータは非常に強力な機能であり、コードの構造や動作を柔軟にカスタマイズできる点が大きな特徴です。

ここでは、デコレータを使って特定の動作をカスタマイズする方法をいくつかの具体的な例を交えて解説します。

○カスタムデコレータの作成

デコレータをカスタマイズするための最も基本的な方法は、自分自身でデコレータを作成することです。

このコードでは、特定のメソッドが呼び出される前にメッセージを表示するデコレータを作成しています。

function ShowMessage(message: string) {
  return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    descriptor.value = function(...args: any[]) {
      console.log(message);
      return originalMethod.apply(this, args);
    };
    return descriptor;
  }
}

このコードを実行すると、ShowMessageデコレータを持つメソッドが呼び出される前に指定されたメッセージが表示されます。

たとえば、次のようにクラス内のメソッドに対してデコレータを適用することができます。

class Sample {
  @ShowMessage("メソッドが呼び出されました!")
  myMethod() {
    console.log("This is myMethod.");
  }
}

const instance = new Sample();
instance.myMethod();

このコードを実行すると、まず”メソッドが呼び出されました!”というメッセージが表示され、次に”This is myMethod.”というメッセージが表示されます。

○デコレータのカスタマイズ例

デコレータはさまざまな場面で応用されるため、その使用方法やカスタマイズの方法も無限大です。

それでは、デコレータのカスタマイズの一例を紹介します。

このコードでは、メソッドの実行にかかる時間を計測し、その結果を表示するデコレータを作成しています。

function TimeMeasure(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function(...args: any[]) {
    const startTime = Date.now();
    const result = originalMethod.apply(this, args);
    const endTime = Date.now();
    console.log(`実行時間: ${endTime - startTime}ミリ秒`);
    return result;
  };
  return descriptor;
}

このコードを実行すると、TimeMeasureデコレータを持つメソッドの実行時間がミリ秒単位で表示されます。

例として、次のようなコードでこのデコレータを使用します。

class Calculation {
  @TimeMeasure
  complexCalculation() {
    // 重い計算をする仮定のコード
    for(let i=0; i<1000000; i++) {}
    console.log("計算完了");
  }
}

const calc = new Calculation();
calc.complexCalculation();

計算完了というメッセージの後に、実行時間がミリ秒単位で表示されます。

まとめ

TypeScriptのデコレータは、クラス、メソッド、アクセサ、プロパティなど、多岐にわたる部分での動作を修飾・強化するための機能を提供します。

デコレータの活用により、コードの再利用性を向上させるだけでなく、読みやすく整理されたコードを実現することができます。

これからも、TypeScriptのデコレータを活用して、効率的かつ品質の高いコードを書いていきましょう。