はじめに
この記事は【TypeScript】+【デコレータ】についての備忘録である。
【デコレータ】とは
デコレータとはメタプログラミングに役に立つ機能である。
メタプログラミングとは、ユーザが直接触ったり見たりする機能には使われないが、開発者が使いやすい道具を提供することに向いている。
JavaのSpringなどを触ったことの有る方はアノテーション@ がイメージしやすい。
- デコレータはメタプログラミングに役立つ機能
- メタプログラミングとは
- ユーザが直接触ったり見たりする機能には使われず、開発者が使いやすい道具を提供することに向いている
- クラスやクラスのメソッドが正しく使われることを保証
- 表向きには見えない変換処理を行う
- デコレータはあてる場所によって受け取れる引数などが変わる
デコレータを追加できる場所
デコレータは Class
内のほぼ全てに追加できる。
- class
- property
- accessor(getter/setter)
- method
- parameter
クラス・デコレータ: Class Decorators
クラス宣言の直前に宣言される。
実行時に関数として呼び出され、デコレータされたクラスのコンストラクターが唯一の引数として使用される。
以下の例では @sealed
デコレータは BugReport
クラスに適用され、そのクラスとプロトタイプがシールド(変更不可)になります。
// デコレータ
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
// クラス・デコレータ使用箇所
@sealed
class BugReport {
type = "report";
title: string;
constructor(t: string) {
this.title = t;
}
}
プロパティ・デコレータ: Property Decorators
プロパティ宣言の直前に宣言される。
実行時に次の 2 つの引数を持つ関数として呼び出される。
- 静的メンバー(static)の場合はクラスのコンストラクター関数、インスタンスメンバー(static以外)の場合はクラスのプロトタイプのいずれか。
- プロパティ名
この例では @format
デコレータは greeting
プロパティに適用され、そのプロパティがアクセスされる際にフォーマットされた文字列が返されるようになる。
// デコレータ
function format(formatString: string) {
return function (target: any, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
get: function () {
return formatString.replace("%s", this[propertyKey]);
},
});
};
}
class Greeter {
// プロパティ・デコレータ使用箇所
@format("Hello, %s")
greeting: string;
constructor(message: string) {
this.greeting = message;
}
}
メソッド・デコレータ: Method Decorators
クラスのメソッド宣言の直前に宣言される。
実行時に次の 3 つの引数を持つ関数として呼び出される。
- 静的メンバー(static)の場合はクラスのコンストラクター関数、インスタンスメンバー(static以外)の場合はクラスのプロトタイプのいずれか。
- プロパティ名
- メソッドの
Property Descriptor
この例では @enumerable
デコレータは greet
メソッドに適用され、そのメソッドが列挙可能かどうかを設定している。
// デコレータ
function enumerable(value: boolean) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
descriptor.enumerable = value;
};
}
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
// メソッド・デコレータ使用箇所
@enumerable(false)
greet() {
return "Hello, " + this.greeting;
}
}
パラメータ・デコレータ: Parameter Decorators
クラスのメソッド宣言内のパラメータの直前に宣言される。
実行時に次の 3 つの引数を持つ関数として呼び出される。
- 静的メンバー(static)の場合はクラスのコンストラクター関数、インスタンスメンバー(static以外)の場合はクラスのプロトタイプのいずれか。
- プロパティ名
- 関数のパラメータインデックス。
この例では @required
デコレータは verbose
パラメータに適用され、必須の有無を設定している。
import "reflect-metadata";
const requiredMetadataKey = Symbol("required");
// デコレータ
function required(target: Object, propertyKey: string | symbol, parameterIndex: number) {
let existingRequiredParameters: number[] = Reflect.getOwnMetadata(requiredMetadataKey, target, propertyKey) || [];
existingRequiredParameters.push(parameterIndex);
Reflect.defineMetadata( requiredMetadataKey, existingRequiredParameters, target, propertyKey);
}
class BugReport {
type = "report";
title: string;
constructor(t: string) {
this.title = t;
}
// パラメータ・デコレータ
print(@required verbose: boolean) {
if (verbose) {
return `type: ${this.type}\ntitle: ${this.title}`;
} else {
return this.title;
}
}
}
アクセサー・デコレータ: Accessor Decorators
クラスのアクセサ宣言の直前に宣言される。
実行時に次の 3 つの引数を持つ関数として呼び出される。
- 静的メンバー(static)の場合はクラスのコンストラクター関数、インスタンスメンバー(static以外)の場合はクラスのプロトタイプのいずれか。
- プロパティ名
- メソッドの
Property Descriptor
この例では @configurable
デコレータは x,y
アクセサーに適用され、アクセサ内に紐づくプロパティの可不可を設定している。
// デコレータ
function configurable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.configurable = value;
};
}
class Point {
private _x: number;
private _y: number;
constructor(x: number, y: number) {
this._x = x;
this._y = y;
}
@configurable(false)
get x() {
return this._x;
}
@configurable(false)
get y() {
return this._y;
}
}
コメント