翻訳者から:TypeScriptはかなり若く、急速に発展している言語です。 残念ながら、インターネットのロシア語部分には、彼に関するかなりの情報がありますが、それはその人気に寄与していません。現在ES6に実装されている多くの機能は、TypeScriptのはるか以前に登場しました。 さらに、いくつかの機能と提案されているES7標準も、この言語で実験的に実装されています。 比較的最近登場したそれらの1つ-デコレータ-について説明します。レモH.ヤンセンが執筆したTypeScriptデコレータに関する記事(または、一連の記事)の翻訳に注目してください。少し前、MicrosoftとGoogle
は TypeScriptとAngular 2.0の
コラボレーションを
発表しました 。
TypeScriptとAtScriptを組み合わせ、サイトおよびWebアプリケーションを作成するための人気のあるJavaScriptライブラリの次のバージョンであるAngular 2がTypeScriptで開発されることをお知らせします

注釈とデコレータ
このコラボレーションは、TypeScriptが新しい言語機能を開発するのに役立ちました。その中で、
注釈を強調しています。
アノテーションは、依存性注入またはコンパイラディレクティブで使用するために、クラス宣言にメタデータを追加する方法です。
注釈はGoogleのAtScriptチームによって提案されましたが、標準ではありません。 一方、デコレータは、設計時にクラスとプロパティを変更するためのECMAScript 7の標準として提案されました。
注釈とデコレータは非常に似ています:
注釈とデコレータはほぼ同じです。 ユーザーの観点から見ると、それらはまったく同じ構文を持っています。 違いは、注釈がコードにメタデータを追加する方法を制御しないことです。 デコレータは、注釈のように動作する何かを構築するためのインターフェイスと考えることができます。
ただし、長い目で見れば、デコレータは既存の標準案であるため、デコレータに焦点を当てます。 AtScriptはTypeScriptであり、TypeScriptはデコレーターを実装します。
TypeScriptのデコレータの構文を見てみましょう。
TypeScriptデコレータ
TypeScriptソースコードでは、利用可能なデコレータタイプの署名を見つけることができます:
declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void; declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void; declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void; declare type ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number) => void;
ご覧のとおり、
クラス 、
プロパティ 、
メソッド、または
パラメーターに注釈を付けるために使用でき
ます 。 これらの各タイプを詳しく見てみましょう。
メソッドデコレータ
デコレータシグネチャがどのように見えるかがわかったので、それらを実装してみましょう。
メソッドdecoratorから始めましょう。 「log」というメソッドデコレータを作成しましょう。
メソッドを宣言する前にデコレータを呼び出すには、シンボル「@」を記述してから、使用するデコレータの名前を記述する必要があります。 デコレータが「ログ」と呼ばれる場合、この構文は次のようになります。
class C { @log foo(n: number) { return n * 2; } }
@log
を使用する前に、アプリケーションのどこかでデコレータ自体を宣言する必要があります。 その実装を見てみましょう。
function log(target: Function, key: string, value: any) { return { value: function (...args: any[]) { var a = args.map(a => JSON.stringify(a)).join(); var result = value.value.apply(this, args); var r = JSON.stringify(result); console.log(`Call: ${key}(${a}) => ${r}`); return result; } }; }
メソッドデコレータは3つの引数を取ります。
ちょっと変だよね?
C
クラス宣言で
@log
デコレータを使用した場合、これらのパラメータは渡しませんでした。 この点に関して、2つの疑問が生じ
ます。これらの議論を誰が通しますか? そして
、ログメソッドは正確にどこで呼び出されますか?それらの答えは、上記の例のためにTypeScriptが生成するコードを見ることで見つけることができます。
var C = (function () { function C() { } C.prototype.foo = function (n) { return n * 2; }; Object.defineProperty(C.prototype, "foo", __decorate([ log ], C.prototype, "foo", Object.getOwnPropertyDescriptor(C.prototype, "foo"))); return C; })();
@log
デコレータがない場合、クラス
C
に対して生成されるJavaScriptコードは次のようになります。
var C = (function () { function C() { } C.prototype.foo = function (n) { return n * 2; }; return C; })();
ご覧の
@log
追加すると、TypeScriptコンパイラによって次のコードがクラス定義に追加されます。
Object.defineProperty(C.prototype, "foo", __decorate( [log], // decorators C.prototype, // target "foo", // key Object.getOwnPropertyDescriptor(C.prototype, "foo") // desc ); );
ドキュメントを参照すると、definePropertyメソッドについて次のことがわかります。
Object.defineProperty()
メソッドは、新しいものを定義するか、オブジェクトの既存のプロパティを直接変更し、このオブジェクトを返します。
TypeScriptコンパイラーは、プロトタイプ
C
、装飾されたメソッドの名前( 'foo')、および
__decorate
関数の結果を
__decorate
メソッドに
__decorate
ます。
defineProperty
、装飾されたメソッドをオーバーライドするために使用されます。 メソッドの新しい実装は、
__decorate
関数の結果です。 新しい疑問が生じます:
__decorate
関数__decorate
どこで__decorate
ますか?以前にTypeScriptを使用したことがある場合、
extends
を使用すると、コンパイラによって
__extends
呼ばれる
__extends
生成されることに気付くかもしれません。
var __extends = this.__extends || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } __.prototype = b.prototype; d.prototype = new __(); };
同様に、デコレータを使用すると、
__decorate
という関数がTypeScriptコンパイラによって生成されます。 彼女を見てみましょう。
var __decorate = this.__decorate || function (decorators, target, key, desc) { if (typeof Reflect === "object" && typeof Reflect.decorate === "function") { return Reflect.decorate(decorators, target, key, desc); } switch (arguments.length) { case 2: return decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target); case 3: return decorators.reduceRight(function(o, d) { return (d && d(target, key)), void 0; }, void 0); case 4: return decorators.reduceRight(function(o, d) { return (d && d(target, key, o)) || o; }, desc); } };
このリストの最初の行では、
OR
演算子を使用して、複数回生成された
__decorate
関数が
__decorate
上書きされないようにします。 2行目では、状態に気付くことができます。
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
この条件は、将来のJavaScript機能(
メタデータリフレクションAPI)を検出するために使用され
ます 。 この一連の記事の最後で詳しく見ていきます。
ちょっと立ち止まって、私たちがこの瞬間に来た経緯を思い出しましょう。
foo
メソッドは、次のパラメーターで呼び出される
__decorate
関数の結果によってオーバーライドされます。
__decorate( [log], // decorators C.prototype, // target "foo", // key Object.getOwnPropertyDescriptor(C.prototype, "foo") // desc );
これで
__decorate
関数の内部になり、メタデータリフレクションAPIが利用できないため、コンパイラによって生成されたバージョンが実行されます
// arguments.length === , __decorate() switch (arguments.length) { case 2: return decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target); case 3: return decorators.reduceRight(function(o, d) { return (d && d(target, key)), void 0; }, void 0); case 4: return decorators.reduceRight(function(o, d) { return (d && d(target, key, o)) || o; }, desc); }
この場合、4つの引数が
__decorate
メソッドに渡されたため、最後のオプションが選択されます。 このコードを扱うのは無意味な変数名のために簡単ではありませんが、私たちは恐れていませんか?
まず、
reduceRightメソッド
が何をするのかを見てみましょう
。reduceRight
は、累積関数を配列の各要素に(右から左の順に)適用し、単一の値を返します。
以下のコードはまったく同じ操作を実行しますが、理解しやすいように書き直されています。
[log].reduceRight(function(log, desc) { if(log) { return log(C.prototype, "foo", desc); } else { return desc; } }, Object.getOwnPropertyDescriptor(C.prototype, "foo"));
このコードが実行されると、
log
デコレーターが呼び出され、それに渡されるパラメーター
C.prototype
、
"foo"
および
previousValue
ます。 つまり、これで質問に対する答えがわかりました。
- これらの議論はどこから来たのですか?
log
関数はどこで呼び出されますか?
log
デコレータの実装に戻ると、呼び出されたときに何が起こるかをよりよく理解できます。
関数ログ(ターゲット:関数、キー:文字列、値:任意){
// target === C.prototype // key === "foo" // value === Object.getOwnPropertyDescriptor(C.prototype, "foo") return { value: function (...args: any[]) { // , foo, var a = args.map(a => JSON.stringify(a)).join(); // foo() var result = value.value.apply(this, args); // var r = JSON.stringify(result); // console.log(`Call: ${key}(${a}) => ${r}`); // foo return result; } };
}
foo
後、
foo
メソッドは通常どおり動作し続けますが、その呼び出しは
log
デコレーターに追加された追加のロギング機能もトリガーし
log
var c = new C(); var r = c.foo(23);
結論
いい冒険だよね? 私と同じように楽しんでいただけたでしょうか。 私たちは始めたばかりで、すでにいくつかの本当にクールなことをする方法を学びました。
メソッドデコレータは、さまざまな興味深い「チップ」に使用できます。 たとえば、
SinonJSのようなテストフレームワークで
"" (spy
を使用した場合、
@spy
デコレータを追加するだけで、デコレータを使用してスパイを作成できる可能性があります。
次のパートでは、
プロパティデコレータの使用方法を学習し
ます 。