maesblog

TypeScriptのDecoratorについて – 公式ドキュメント日本語訳

Angularを勉強していると、至る所でTypeScriptの知識が必要になってきます。ということで、TypeScriptの公式ドキュメントにお世話になる機会も増えてきています。前回はClassの説明ページを日本語に訳しましたが、今回はDecoratorの説明ページ(Decorators・TypeScript)を日本語に訳しました。Angularでは、単なるクラスをAngularで使えるコンポーネントとしてその役割を担わせる時などにデコレータを使用したりします。けっこう大事な部分でもあるので、日本語に翻訳することにしました。ぜひ参考にしてもらえればと思います。

Introduction: はじめに

With the introduction of Classes in TypeScript and ES6, there now exist certain scenarios that require additional features to support annotating or modifying classes and class members. Decorators provide a way to add both annotations and a meta-programming syntax for class declarations and members. Decorators are a stage 2 proposal for JavaScript and are available as an experimental feature of TypeScript.

TypeScriptやES6のクラスを導入するに当たって、クラスやクラスのメンバーに対してアノテーションや修正をサポートする機能を追加する必要に迫られます。デコレータはこれらのアノテーションやメタプログラミングの構文をクラスの宣言やメンバーのために追加する方法を提供します。デコレータはJavaScriptのstage 2 proposalの段階にあり、TypeScriptではexperimental feature(実験的機能)として利用できます。

NOTE  Decorators are an experimental feature that may change in future releases.

デコレータはexperimental featureであり、今後のリリースにおいて変更される可能性があるのでご注意ください。

To enable experimental support for decorators, you must enable the experimentalDecorators compiler option either on the command line or in your tsconfig.json:

デコレータのexperimental support(実験的サポート)を有効にするには、コンパイラーオプションとしてexperimentalDecoratorsをコマンドライン上でか、またはtsconfig.jsonの中で有効にする必要があります。

Command Line:

tsc --target ES5 --experimentalDecorators

tsconfig.json:

{
  "compilerOptions": {
    "target": "ES5",
    "experimentalDecorators": true
  }
}

Decorators: デコレータ

A Decorator is a special kind of declaration that can be attached to a class declaration, method, accessor, property, or parameter. Decorators use the form @expression, where expression must evaluate to a function that will be called at runtime with information about the decorated declaration.

デコレータはクラスの宣言メソッドアクセサプロパティパラメーターに結び付けられる特別な宣言の一種です。デコレータは@expressionという形式で使います。expressionは、デコレートされた宣言の情報を持ち、実行時に呼び出される関数として評価されます。

For example, given the decorator @sealed we might write the sealed function as follows:

例えば、@sealedというデコレータが与えられたとしたら、以下のようなsealed関数を書くことになります。

function sealed(target) {
  // do something with 'target' ...
}

NOTE  You can see a more detailed example of a decorator in Class Decorators, below.

より詳細なクラス・デコレータを使ったデコレータの例は以下で確認できます。

Decorator Factories: デコレータ・ファクトリー

If we want to customize how a decorator is applied to a declaration, we can write a decorator factory. A Decorator Factory is simply a function that returns the expression that will be called by the decorator at runtime.

デコレータが宣言に適用される方法をカスタマイズしたいなら、デコレータ・ファクトリーを書くことができます。Decorator Factory(デコレータファクトリー)は、実行時にデコレータによって呼び出される式を返すだけのシンプルな関数です。

We can write a decorator factory in the following fashion:

デコレータ・ファクトリーは、以下のような感じで書くことができます。

function color(value: string) { // this is the decorator factory
  return function (target) { // this is the decorator
    // do something with 'target' and 'value'...
  }
}

NOTE  You can see a more detailed example of a decorator factory in Method Decorators, below.

より詳細なメソッド・デコレータを使ったデコレータ・ファクトリーの例は以下で確認できます。

Decorator Composition: デコレータの構成

Multiple decorators can be applied to a declaration, as in the following examples:

複数のデコレータを宣言に適用させるには、以下の例のように書きます。

  • On a single line:

    1行で書く場合

    @f @g x
  • On multiple lines:

    複数行で書く場合

    @f
    @g
    x
    

When multiple decorators apply to a single declaration, their evaluation is similar to function composition in mathematics. In this model, when composing functions f and g, the resulting composite (f ∘ g)(x) is equivalent to f(g(x)).

複数のデコレータをひとつの宣言に適用する時、デコレータの評価は数学の関数合成(function composition)と似たようなものになります。このモデルでは、fgの関数を合成する時、(f ∘ g)(x)の合成の結果は、f(g(x))と等しいものになります。

As such, the following steps are performed when evaluating multiple decorators on a single declaration in TypeScript:

したがって、TypeScriptでひとつの宣言に対して複数のデコレータを評価する時は、以下のステップを踏みます。

1. The expressions for each decorator are evaluated top-to-bottom.
2. The results are then called as functions from bottom-to-top.

  1. まず式が上から下に向かって評価される
  2. 次に関数として結果が下から上へ向かって呼び出される

If we were to use decorator factories, we can observe this evaluation order with the following example:

デコレータ・ファクトリーを使うとしたら、以下の例を使って評価の順番を検査することができます。

function f() {
  console.log("f(): evaluated");
  return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log("f(): called");
  }
}

function g() {
  console.log("g(): evaluated");
  return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log("g(): called");
  }
}

class C {
  @f()
  @g()
  method() {}
}

Which would print this output to the console:

この結果は、以下のようにコンソールに出力されます。

f(): evaluated
g(): evaluated
g(): called
f(): called

Decorator Evaluation: デコレータの評価

There is a well defined order to how decorators applied to various declarations inside of a class are applied:

1. Parameter Decorators, followed by Method, Accessor, or Property Decorators are applied for each instance member.
2. Parameter Decorators, followed by Method, Accessor, or Property Decorators are applied for each static member.
3. Parameter Decorators are applied for the constructor.
4. Class Decorators are applied for the class.

デコレータがどのようにクラス内のさまざまな宣言に適用されるかは、以下のような明確に定義されたルールがあります。

  1. パラメーター・デコレータ、メソッド・デコレータ、アクセサ・デコレータ、プロパティ・デコレータの順番でインスタンスメンバーに適用される。
  2. パラメーター・デコレータ、メソッド・デコレータ、アクセサ・デコレータ、プロパティ・デコレータの順番でstatic(静的)メンバーに適用される。
  3. パラメーター・デコレータはコンストラクタに適用される。
  4. クラス・デコレータはクラスに適用される。

Class Decorators: クラス・デコレータ

A Class Decorator is declared just before a class declaration. The class decorator is applied to the constructor of the class and can be used to observe, modify, or replace a class definition. A class decorator cannot be used in a declaration file, or in any other ambient context (such as on a declare class).

Class Decorator(クラス・デコレータ)は、クラス宣言の直前で宣言します。クラス・デコレータはクラスのコンストラクタに適用され、クラスの定義の検査、修正、置換に使われます。クラス・デコレータは、宣言ファイル内や、他のいかなるコンテキスト(例えばdeclareクラスのようなクラス)で使われることはありません。

The expression for the class decorator will be called as a function at runtime, with the constructor of the decorated class as its only argument.

クラス・デコレータの式は、実行時に関数として呼び出され、その関数には、デコレートされるクラスのコンストラクタが唯一の引数として渡されます。

If the class decorator returns a value, it will replace the class declaration with the provided constructor function.

クラス・デコレータに値を返させることで、もともとのクラスの宣言を、クラス・デコレータが返すコンストラクタ関数で置き換えられるようになります。

NOTE Should you chose to return a new constructor function, you must take care to maintain the original prototype. The logic that applies decorators at runtime will not do this for you.

もしクラス・デコレータが新しいコンストラクタ関数を返すようにしたら、もともとのprototypeを維持することに気を付ける必要があります。実行時にデコレータを適用するロジックは、それを行ってくれません

The following is an example of a class decorator (@sealed) applied to the Greeter class:

以下は、Greeterクラスに適用されるクラス・デコレータ(@sealed)の例です。

@sealed
class Greeter {
  greeting: string;
  constructor(message: string) {
    this.greeting = message;
  }
  greet() {
    return "Hello, " + this.greeting;
  }
}

We can define the @sealed decorator using the following function declaration:

@sealedデコレータを以下のような関数の宣言にして定義できます。

function sealed(constructor: Function) {
  Object.seal(constructor);
  Object.seal(constructor.prototype);
}

When @sealed is executed, it will seal both the constructor and its prototype.

上記の例では、@sealedが実行されると、constructorとconstructor.prototypeの両方がObject.sealにより封印されるようになります。

Next we have an example of how to override the constructor.

次に、以下はコンストラクタを上書きする方法についての例です。

function classDecorator<T extends {new(...args:any[]):{}}>(constructor:T) {
  return class extends constructor {
    newProperty = "new property";
    hello = "override";
  }
}

@classDecorator
class Greeter {
  property = "property";
  hello: string;
  constructor(m: string) {
    this.hello = m;
  }
}

console.log(new Greeter("world"));

Method Decorators: メソッド・デコレータ

A Method Decorator is declared just before a method declaration. The decorator is applied to the Property Descriptor for the method, and can be used to observe, modify, or replace a method definition. A method decorator cannot be used in a declaration file, on an overload, or in any other ambient context (such as in a declare class).

Method Decorator(メソッド・デコレータ)はメソッドの宣言の直前で宣言します。デコレータはメソッドのProperty Descriptor(プロパティ・ディスクリプタ)に適用され、メソッドの定義の検査、修正、置換に使われます。メソッド・デコレータは、宣言ファイル内や、上書き時、他のいかなるコンテキスト(例えばdeclareクラスのようなクラス内)で使われることはありません。

The expression for the method decorator will be called as a function at runtime, with the following three arguments:

メソッド・デコレータの式は、実行時に関数として呼び出され、その関数には、以下の3つの引数が渡されます。

1. Either the constructor function of the class for a static member, or the prototype of the class for an instance member.
2. The name of the member.
3. The Property Descriptor for the member.

  1. 宣言したメソッドがstaticメンバーの場合はクラスのコンストラクタ関数、インスタンスメンバーの場合はクラスのprototype
  2. メンバー名
  3. メンバーのProperty Descriptor(プロパティ・ディスクリプタ)

NOTE  The Property Descriptor will be undefined if your script target is less than ES5.

スクリプトの出力先のtarget設定がES5未満の場合、Property Descriptor(プロパティ・ディスクリプタ)はundefinedとなるのでご注意ください。

If the method decorator returns a value, it will be used as the Property Descriptor for the method.

メソッド・デコレータに値を返させると、メソッド・デコレータの戻り値はメソッドのProperty Descriptor(プロパティ・ディスクリプタ)として使われるようになります。

NOTE  The return value is ignored if your script target is less than ES5.

スクリプトの出力先のtarget設定がES5未満の場合、戻り値は無視されるのでご注意ください。

The following is an example of a method decorator (@enumerable) applied to a method on the Greeter class:

以下は、Greeterクラスのメソッドに適用されるメソッド・デコレータ(@enumerable)の例です。

class Greeter {
  greeting: string;
  constructor(message: string) {
    this.greeting = message;
  }

  @enumerable(false)
  greet() {
    return "Hello, " + this.greeting;
  }
}

We can define the @enumerable decorator using the following function declaration:

@enumerableデコレータを以下のような関数の宣言にして定義することができます。

function enumerable(value: boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    descriptor.enumerable = value;
  };
}

The @enumerable(false) decorator here is a decorator factory. When the @enumerable(false) decorator is called, it modifies the enumerable property of the property descriptor.

この@enumerable(false)デコレータはデコレータ・ファクトリーです。@enumerable(false)デコレータが呼び出されると、@enumerable(false)デコレータはプロパティ・ディスクリプタのenumerableプロパティ値を修正します。

Accessor Decorators: アクセサ・デコレータ

An Accessor Decorator is declared just before an accessor declaration. The accessor decorator is applied to the Property Descriptor for the accessor and can be used to observe, modify, or replace an accessor’s definitions. An accessor decorator cannot be used in a declaration file, or in any other ambient context (such as in a declare class).

Accessor Decorator(アクセサ・デコレータ)は、アクセサの宣言の直前で宣言します。アクセサ・デコレータはアクセサのProperty Descriptor(プロパティ・ディスクリプタ)に適用され、アクセサの定義の検査、修正、置換に使われます。アクセサ・デコレータは、宣言ファイル内や、他のいかなるコンテキスト(例えばdeclareクラスの中)で使われることはありません。

NOTE  TypeScript disallows decorating both the get and set accessor for a single member. Instead, all decorators for the member must be applied to the first accessor specified in document order. This is because decorators apply to a Property Descriptor, which combines both the get and set accessor, not each declaration separately.

TypeScriptは1つのメンバーのgetsetの両方のアクセサをデコレートすることができないのでご注意ください。その代わりに、メンバーに対してすべてのデコレータは、documentの順番で指定された最初のアクセサの方に適用させる必要があります。なぜならデコレータはプロパティ・ディスクリプタに適用されるからです。つまり、プロパティ・ディスクリプタは、getsetの両方のアクセサを組み合わせて持ち、それぞれの宣言を別々にはしないからです。

The expression for the accessor decorator will be called as a function at runtime, with the following three arguments:

1. Either the constructor function of the class for a static member, or the prototype of the class for an instance member.
2. The name of the member.
3. The Property Descriptor for the member.

アクセサ・デコレータの式は、実行時に関数として呼び出され、その関数には、以下の3つの引数が渡されます。

  1. 宣言したアクセサがstaticメンバーの場合はクラスのコンストラクタ関数、インスタンスメンバーの場合はクラスのprototype
  2. メンバー名
  3. メンバーのProperty Descriptor(プロパティ・ディスクリプタ)

NOTE  The Property Descriptor will be undefined if your script target is less than ES5.

スクリプトの出力先のtarget設定がES5未満の場合、プロパティ・ディスクリプタはundefinedとなるのでご注意ください。

If the accessor decorator returns a value, it will be used as the Property Descriptor for the member.

アクセサ・デコレータに値を返させると、アクセサ・デコレータの戻り値はメンバーのProperty Descriptor(プロパティ・ディスクリプタ)として使われるようになります。

NOTE  The return value is ignored if your script target is less than ES5.

スクリプトの出力先のtarget設定がES5未満の場合、戻り値は無視されるのでご注意ください。

The following is an example of an accessor decorator (@configurable) applied to a member of the Point class:

以下は、Pointクラスのメンバーに適用されるアクセサ・デコレータ(@configurable)の例です。

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; }
}

We can define the @configurable decorator using the following function declaration:

@configurableデコレータを以下のような関数の宣言にして定義することができます。

function configurable(value: boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    descriptor.configurable = value;
  };
}

Property Decorators: プロパティ・デコレータ

A Property Decorator is declared just before a property declaration. A property decorator cannot be used in a declaration file, or in any other ambient context (such as in a declare class).

Property Decorator(プロパティ・デコレータ)は、プロパティの宣言の直前で宣言します。プロパティ・デコレータは、宣言ファイル内や、他のいかなるコンテキスト(例えばdeclareクラスのようなクラス内)で使われることはありません。

The expression for the property decorator will be called as a function at runtime, with the following two arguments:

1. Either the constructor function of the class for a static member, or the prototype of the class for an instance member. 2. The name of the member.

プロパティ・デコレータの式は、実行時に関数として呼び出され、その関数には、以下の2つの引数が渡されます。

  1. 宣言したプロパティがstaticメンバーの場合はクラスのコンストラクタ関数、インスタンスメンバーの場合はクラスのprototype
  2. メンバー名

NOTE  A Property Descriptor is not provided as an argument to a property decorator due to how property decorators are initialized in TypeScript. This is because there is currently no mechanism to describe an instance property when defining members of a prototype, and no way to observe or modify the initializer for a property. As such, a property decorator can only be used to observe that a property of a specific name has been declared for a class.

プロパティ・ディスクリプタは引数としてプロパティ・デコレータに渡されないのでご注意ください。これは、プロパティ・デコレータのTypeScriptにおける初期化方法に起因しています。つまり現在のところ、プロパティのメンバーを定義する時に、インスタンス・プロパティを記述するメカニズムがなく、プロパティのイニシャライザ(初期化演算子)を検査、または修正する方法がないからです。従って、プロパティ・デコレータは、特定の名前のプロパティがクラスで宣言されているか検査するためだけに使われます。

If the property decorator returns a value, it will be used as the Property Descriptor for the member.

プロパティ・デコレータに値を返させると、プロパティ・デコレータの戻り値はメンバーのProperty Descriptor(プロパティ・ディスクリプタ)として使われるようになります。

NOTE  The return value is ignored if your script target is less than ES5.

スクリプトの出力先のtarget設定がES5未満の場合、戻り値は無視されるのでご注意ください。

We can use this information to record metadata about the property, as in the following example:

プロパティのメタデータを記録する方法として、以下の例のように書くことができます。

class Greeter {
  @format("Hello, %s")
  greeting: string;

  constructor(message: string) {
    this.greeting = message;
  }
  greet() {
    let formatString = getFormat(this, "greeting");
    return formatString.replace("%s", this.greeting);
  }
}

We can then define the @format decorator and getFormat functions using the following function declarations:

@formatデコレータとgetFormat関数を以下のような関数の宣言にして定義できます。

import "reflect-metadata";

const formatMetadataKey = Symbol("format");

function format(formatString: string) {
  return Reflect.metadata(formatMetadataKey, formatString);
}

function getFormat(target: any, propertyKey: string) {
  return Reflect.getMetadata(formatMetadataKey, target, propertyKey);
}

The @format(“Hello, %s”) decorator here is a decorator factory. When @format(“Hello, %s”) is called, it adds a metadata entry for the property using the Reflect.metadata function from the reflect-metadata library. When getFormat is called, it reads the metadata value for the format.

この@format(“Hello, %s”)デコレータは、デコレータ・ファクトリーです。@format(“Hello, %s”)が呼び出されると、@format(“Hello, %s”)デコレータは、インポートしたreflect-metadataライブラリのReflect.metadata関数を使ってプロパティのメタデータを追加します。getFormatが呼び出されると、getFormatはformatのメタデータの値を取得します。

NOTE  This example requires the reflect-metadata library. See Metadata for more information about the reflect-metadata library.

この例では、reflect-metadataライブラリを必要とするのでご注意ください。reflect-metadataライブラリに関するより詳細な情報についてはメタデータをご参照ください。

Parameter Decorators: パラメーター・デコレータ

A Parameter Decorator is declared just before a parameter declaration. The parameter decorator is applied to the function for a class constructor or method declaration. A parameter decorator cannot be used in a declaration file, an overload, or in any other ambient context (such as in a declare class).

Parameter Decorator(パラメーター・デコレータ)は、パラメーターの宣言の直前で宣言します。パラメーター・デコレータはクラスのコンストラクタやメソッドの宣言に適用されます。パラメーター・デコレータは、宣言ファイル内や、上書き時、他のいかなるコンテキスト(例えばdeclareクラスのようなクラス内)で使われることはありません。

The expression for the parameter decorator will be called as a function at runtime, with the following three arguments:

1. Either the constructor function of the class for a static member, or the prototype of the class for an instance member.
2. The name of the member.
3. The ordinal index of the parameter in the function’s parameter list.

パラメーター・デコレータの式は、実行時に関数として呼び出され、その関数には、以下の3つの引数が渡されます。

  1. 宣言したパラメータを指定しているメソッドがstaticメンバーの場合はクラスのコンストラクタ関数、インスタンスメンバーの場合はクラスのprototype
  2. メンバー名
  3. 関数のパラメーターリストにおけるパラメーターのオーディナル・インデックス(順序を表すインデックス)

NOTE  A parameter decorator can only be used to observe that a parameter has been declared on a method.

パラメーター・デコレータはパラメーターがメソッドで宣言されているか検査するためだけに使われます。

The return value of the parameter decorator is ignored.

パラメーター・デコレータの戻り値は無視されます。

The following is an example of a parameter decorator (@required) applied to parameter of a member of the Greeter class:

以下は、Greeterクラスのメンバーのパラメーターに適用されるパラメーター・デコレータ(@required)の例です。

class Greeter {
  greeting: string;

  constructor(message: string) {
    this.greeting = message;
  }

  @validate
  greet(@required name: string) {
    return "Hello " + name + ", " + this.greeting;
  }
}

We can then define the @required and @validate decorators using the following function declarations:

@requiredデコレータと@validateデコレータを以下のような関数の宣言にして定義できます。

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);
}

function validate(target: any, propertyName: string, descriptor: TypedPropertyDescriptor) {
  let method = descriptor.value;
  descriptor.value = function () {
    let requiredParameters: number[] = Reflect.getOwnMetadata(requiredMetadataKey, target, propertyName);
    if (requiredParameters) {
      for (let parameterIndex of requiredParameters) {
        if (parameterIndex >= arguments.length || arguments[parameterIndex] === undefined) {
          throw new Error("Missing required argument.");
        }
      }
    }

    return method.apply(this, arguments);
  }
}

The @required decorator adds a metadata entry that marks the parameter as required. The @validate decorator then wraps the existing greet method in a function that validates the arguments before invoking the original method.

@requiredデコレータは、パラメーターを必須項目であると示すメタデータのエントリーを追加します。@validateデコレータは、オリジナルのメソッドを呼び出す前に引数をバリデートする関数内に、既存のgreetメソッドをラップします。

NOTE  This example requires the reflect-metadata library. See Metadata for more information about the reflect-metadata library.

この例では、reflect-metadataライブラリを必要とするのでご注意ください。reflect-metadataライブラリに関するより詳細な情報についてはメタデータをご参照ください。

Metadata: メタデータ

Some examples use the reflect-metadata library which adds a polyfill for an experimental metadata API. This library is not yet part of the ECMAScript (JavaScript) standard. However, once decorators are officially adopted as part of the ECMAScript standard these extensions will be proposed for adoption.

上記のいくつかの例では、実験的なmetadata APIのpolyfillを追加するreflect-metadataライブラリを使用しています。このライブラリはまだECMAScript(JavaScript)standardには含まれていません。しかし、デコレータがECMAScript standardの一部として正式に採用されたら、これらの拡張機能は採用されるように提案されるでしょう。

You can install this library via npm:

このライブラリはnpmを通してインストールします。

npm i reflect-metadata --save

TypeScript includes experimental support for emitting certain types of metadata for declarations that have decorators. To enable this experimental support, you must set the emitDecoratorMetadata compiler option either on the command line or in your tsconfig.json:

TypeScriptには、デコレータを持つ宣言の特殊なメタデータを吐き出すexperimental support(実験的サポート)が含まれています。この実験的サポートを有効にするには、Command Line上でか、またはtsconfig.jsonにコンパイラオプションとしてemitDecoratorMetadataをセットする必要があります。

Command Line:

tsc --target ES5 --experimentalDecorators --emitDecoratorMetadata

tsconfig.json:

{
  "compilerOptions": {
    "target": "ES5",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

When enabled, as long as the reflect-metadata library has been imported, additional design-time type information will be exposed at runtime.

実験的サポートが有効化されると、その後はreflect-metadataライブラリがインポートさえされていれば、追加されたデザインタイムの型情報は実行時に公開されるようになります。

We can see this in action in the following example:

以下の例を実行するとこれを確認することができます。

import "reflect-metadata";

class Point {
  x: number;
  y: number;
}

class Line {
  private _p0: Point;
  private _p1: Point;

  @validate
  set p0(value: Point) { this._p0 = value; }
  get p0() { return this._p0; }

  @validate
  set p1(value: Point) { this._p1 = value; }
  get p1() { return this._p1; }
}

function validate(target: any, propertyKey: string, descriptor: TypedPropertyDescriptor) {
  let set = descriptor.set;
  descriptor.set = function (value: T) {
    let type = Reflect.getMetadata("design:type", target, propertyKey);
    if (!(value instanceof type)) {
      throw new TypeError("Invalid type.");
    }
    set(value);
  }
}

The TypeScript compiler will inject design-time type information using the @Reflect.metadata decorator. You could consider it the equivalent of the following TypeScript:

TypeScriptのコンパイラーは@Reflect.metadataデコレータを使ってデザインタイムの型情報を注入します。それは以下のTypeScriptのコードのようなものとなります。

class Line {
  private _p0: Point;
  private _p1: Point;

  @validate
  @Reflect.metadata("design:type", Point)
  set p0(value: Point) { this._p0 = value; }
  get p0() { return this._p0; }

  @validate
  @Reflect.metadata("design:type", Point)
  set p1(value: Point) { this._p1 = value; }
  get p1() { return this._p1; }
}

NOTE  Decorator metadata is an experimental feature and may introduce breaking changes in future releases.

デコレータのメタデータはexperimental feature(実験的機能)であり、今後のリリースにおいて破壊的変更が行われる可能性があるのでご注意ください。

まとめ

以上、TypeScriptの公式ドキュメント「decoratorの部分(Decorators・TypeScript)」の日本語訳となります。慣れていないとちょっと難しい概念かもしれませんが、実際にコードを書いて、どのデコレータの時にどんな値がデコレータに渡されるかなど1つ1つ確認していくと理解が進むかと思います。

デコレータ・ファクトリーについて

それから、デコレータに関して最初に混乱しそうな部分として、デコレータ・ファクトリーの使い方かなと思ったので、解説を加えておきます。基本的にデコレータを宣言する際に、引数を通して値を渡す場合は、デコレータ・ファクトリーを使って値を受け取るようにします。デコレータ自体はデコレータ・ファクトリーの戻り値として返すようにします。

以下は、引数を渡さないデコレータを宣言する場合の例です。

function foo(target) { // ここがデコレータ
  // 引数の'target'を使って処理を書く ...
}

// @fooデコレータの宣言(引数なし)
@foo
class Baa{
}

以下は、引数を渡すデコレータを宣言する場合の例です。

function foo(value: string) { // ここがデコレータ・ファクトリー
  return function (target) { // ここがデコレータ
    // 'value'と'target'を使って処理を書く
  }
}

// @fooデコレータの宣言(引数あり)
@foo('foo')
class Baa{
}

Angularとデコレータ

Angularではデコレータを使用することになっています。デコレータには、これまで見てきたようにクラス向け、メソッド向け、アクセサ向けなど複数のデコレータが用意されていますが、Angularでよく使うのはクラス・デコレータとなるでしょう。例えば、あるクラスにコンポーネントとしての役割を担わせたい時は、以下のように@componentデコレータを使って、必要な情報を渡します。

@Component({
  selector:    'foo',
  templateUrl: './foo.component.html',
  providers:  [ FooService ]
})
export class FooComponent implements OnInit {
/* . . . */
}

以上のようにAngularではデコレータの知識が必須となるので、まだECMAScript的にも、TypeScript的にも実験的な機能としての位置付けとなっていますが、しっかりと押さえておく必要があります。ぜひ今回の日本語訳を参考にしてもらえれば幸いです。訳がわかりにくい部分がありましたら、ご遠慮なくご連絡ください。

2017-08-06追記: JavaScriptのデコレーターに関してわかりやすくまとめられた記事があったので紹介しておきます。デコレータ関数がどのような仕組みになっているかなど参考になります。

関連記事

コメント

  • 必須

コメント