TypeScriptにおけるデコレータは、クラスやそのメンバーに対し、宣言的にメタデータを付与する機能です。このメタデータは、クラスの特性や振る舞いを外部から修飾するために利用されます。このページでは、TypeScriptのデコレータの基本概念から応用までを解説し、コードの柔軟性と表現力を高める可能性を探ります。
デコレータとは?コードへの注釈による機能拡張
デコレータは、クラス、メソッド、プロパティ、パラメータに対して付与される特殊な構文です。@
記号に続いてデコレータ名を記述することで、対象となるコード要素に対する注釈として機能し、TypeScriptコンパイラや関連フレームワークに対し、その要素の特性や振る舞いに関する情報を提供することになります。
function sealed(constructor: Function) { Object.seal(constructor); Object.seal(constructor.prototype); } // ↓がデコレータ @sealed class MyClass { constructor(public name: string) {} greet() { console.log(`Hello, ${this.name}!`); } }
TypeScriptにおけるデコレータの利点:表現力と保守性の向上
TypeScriptでデコレータを活用することには、複数の利点があります。

使いこなしていくうちに徐々にメリットを感じていけると思いますが、事前に簡単に利点を整理しておきます。
- メタプログラミングの実現
プログラム自身がその構造や実行時の振る舞いを操作するメタプログラミングを可能にする。 - 関心事の分離
ログ記録、認証、トランザクション管理といった横断的な関心事を、主要なビジネスロジックから分離し、コードの可読性と保守性を高めることができる。 - コードの再利用性
一度定義されたデコレータは、複数のクラスやメンバーに適用でき、重複したコードを削減できる。 - フレームワークとの統合
Angularなどのフレームワークは、デコレータを基盤としており、フレームワークの機能利用を簡潔化します。
TypeScriptの主要なデコレータの種類と機能
TypeScriptは、デコレータが適用される対象に応じて、以下の種類を提供します。
1. クラスデコレータ
クラス定義の直前に記述され、クラス全体の振る舞いを変更、またはメタデータを付与します。クラスデコレータは、クラスのコンストラクタ関数を引数として受け取ります。
function sealed(constructor: Function) { Object.seal(constructor); Object.seal(constructor.prototype); } @sealed class MyClass { constructor(public name: string) {} greet() { console.log(`Hello, ${this.name}!`); } } const instance = new MyClass("TypeScript"); instance.greet(); // 出力結果:Hello, TypeScript!
2. メソッドデコレータ
メソッド定義の直前に記述され、メソッドの振る舞いを修飾、またはメタデータを付与します。メソッドデコレータは、ターゲットオブジェクト、メソッド名、プロパティ記述子を引数として受け取ります。
function logMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) { const originalMethod = descriptor.value; descriptor.value = function(...args: any[]) { console.log(`メソッド '${propertyKey}' が引数 ${JSON.stringify(args)} で呼び出されました。`); const result = originalMethod.apply(this, args); console.log(`メソッド '${propertyKey}' の実行結果: ${result}`); return result; }; return descriptor; } class Calculator { @logMethod add(a: number, b: number): number { return a + b; } } const calc = new Calculator(); const sum = calc.add(5, 3); console.log(`合計: ${sum}`); // 出力結果: // メソッド 'add' が引数 [5,3] で呼び出されました。 // メソッド 'add' の実行結果: 8 // 合計: 8
3. プロパティデコレータ
プロパティ定義の直前に記述され、プロパティのメタデータを付与、またはアクセス時の振る舞いを変更します。プロパティデコレータは、ターゲットオブジェクトとプロパティ名を引数として受け取ります。
function readonly(target: any, propertyKey: string) { Object.defineProperty(target, propertyKey, { writable: false }); } class Person { @readonly name: string = "John Doe"; constructor() {} } const person = new Person(); console.log(person.name); // 出力結果:John Doe // person.name = "Jane Doe"; // コンパイルエラーが発生します
function readonly(target: any, propertyKey: string) { Object.defineProperty(target, propertyKey, { writable: false }); } class Person { @readonly name: string = "John Doe"; constructor() {} }
4. パラメータデコレータ
メソッドのパラメータ定義の直前に記述され、パラメータに関するメタデータを付与します。パラメータデコレータは、ターゲットオブジェクト、メソッド名、パラメータのインデックスを引数として受け取ります。
function paramInfo(target: any, methodKey: string, parameterIndex: number) { console.log(`クラス '${target.constructor.name}' のメソッド '${methodKey}' のインデックス ${parameterIndex} 番目のパラメータです。`); } class Greeter { greet(@paramInfo message: string) { console.log(`Greeting: ${message}`); } } const greeter = new Greeter(); greeter.greet("Hello!"); // 出力結果: // クラス 'Greeter' のメソッド 'greet' のインデックス 0 番目のパラメータです。 // Greeting: Hello!
デコレータの動作原理:メタデータの利用
TypeScriptコンパイラは、デコレータを特別な関数として処理し、デコレートされた要素に関する情報を引数としてデコレータ関数に渡します。
デコレータ関数は、この情報に基づいて、クラスやメンバーの振る舞いを変更したり、メタデータを格納したりします。このメタデータは、TypeScriptランタイムやフレームワークがコードの構造と意図を解釈し、適切な処理を実行するために利用されます。
Angularにおけるデコレータの応用:フレームワークの基礎構造
Angularフレームワークは、TypeScriptのデコレータ機能を広範に活用しています。
@Component
、@Directive
、@Injectable
、@Input
、@Output
などのAngularデコレータは、クラスに特定の役割と機能を持たせるための重要なメカニズムです。例えば、@Component
デコレータは、TypeScriptクラスをUIコンポーネントとして定義し、テンプレートやスタイルといった関連情報をAngularに提供します。