吕立宏 發表於 2019-5-30 01:06:00

TypeScript 装饰器

<table class="d-block">
<tbody class="d-block">
    <tr class="d-block">
      <td class="d-block comment-body markdown-bodyjs-comment-body">
<p>装饰器(Decorators)可用来装饰类,属性,及方法,甚至是函数的参数,以改变和控制这些对象的表现,获得一些功能。</p>
<p>装饰器以 <code>@expression</code> 形式呈现在被装饰对象的前面或者上方,其中 <code>expression</code> 为一个函数,根据其所装饰的对象的不同,得到的入参也不同。</p>
<p>以下两种风格均是合法的:</p>
<div class="highlight highlight-source-ts"><pre>@<span class="pl-smi">f</span> @<span class="pl-smi">g</span> <span class="pl-smi">x</span></pre></div>
<div class="highlight highlight-source-ts"><pre>@<span class="pl-smi">f</span>
@<span class="pl-smi">g</span>
<span class="pl-smi">x</span></pre></div>
<p>ES 中装饰器处于 Stage 2 阶段 ,TypeScript 中通过开启相应编译开关来使用。</p>
<div class="highlight highlight-source-json"><pre>{
<span class="pl-s"><span class="pl-pds">"</span>compilerOptions<span class="pl-pds">"</span></span>: {
    <span class="pl-s"><span class="pl-pds">"</span>target<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>ES5<span class="pl-pds">"</span></span>,
    <span class="pl-s"><span class="pl-pds">"</span>experimentalDecorators<span class="pl-pds">"</span></span>: <span class="pl-c1">true</span>
}
}</pre></div>
<h2>一个简单的示例</h2>
<p>一个简单的示例,展示了 TypeScript 中如何编写和使用装饰器。</p>
<div class="highlight highlight-source-ts"><pre><span class="pl-k">function</span> log(
<span class="pl-v">_target</span><span class="pl-k">:</span> <span class="pl-c1">any</span>,
<span class="pl-v">propertyKey</span><span class="pl-k">:</span> <span class="pl-c1">string</span>,
<span class="pl-v">descriptor</span><span class="pl-k">:</span> <span class="pl-en">PropertyDescriptor</span>
) {
<span class="pl-k"><span class="pl-k">const</span></span> originalMethod <span class="pl-k">=</span> <span class="pl-smi">descriptor</span>.<span class="pl-c1">value</span>;
<span class="pl-smi">descriptor</span>.<span class="pl-c1">value</span> <span class="pl-k">=</span> <span class="pl-k">function</span>() {
    <span class="pl-c1">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">`</span>method ${<span class="pl-smi">propertyKey</span>} called<span class="pl-pds">`</span></span>);
    <span class="pl-k">return</span> <span class="pl-smi">originalMethod</span>.<span class="pl-c1">apply</span>(<span class="pl-c1">this</span>, <span class="pl-c1">arguments</span>);
};
}
<p><span class="pl-k">class</span> <span class="pl-en">Test</span> {<br>
@<span class="pl-smi">log</span><br>
<span class="pl-k">static</span> sayHello() {<br>
<span class="pl-c1">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">"</span>hello<span class="pl-pds">"</span></span>);<br>
}<br>
}</p>
<p><span class="pl-smi">Test</span>.<span class="pl-en">sayHello</span>();</p></pre></div><p></p>
<p>上面的示例中,创建了名为 <code>log</code> 的方法,它将作为装饰器作用于类的方法上,在方法被调用时输出一条日志。作为装饰器的 <code>log</code> 函数其入参在后面会介绍。</p>
<p>执行结果:</p>
<div class="highlight highlight-source-shell"><pre>method sayHello called
hello</pre></div>
<h2>装饰器的工厂方法</h2>
<p>上面的装饰器比较呆板,设想我们想将它变得更加灵活和易于复用一些,则可以通过创建一个工厂方法来实现。因为本质上装饰器就是个普通函数,函数可通过另外的函数来创建和返回,同时装饰器的使用本质上也是一个函数调用。通过传递给工厂方法不同的参数,以获得不同表现的装饰器。</p>
<div class="highlight highlight-source-ts"><pre><span class="pl-k">function</span> logFactory(<span class="pl-v">prefix</span><span class="pl-k">:</span> <span class="pl-c1">string</span>) {
<span class="pl-k">return</span> <span class="pl-k">function</span> log(
    <span class="pl-v">_target</span><span class="pl-k">:</span> <span class="pl-c1">any</span>,
    <span class="pl-v">propertyKey</span><span class="pl-k">:</span> <span class="pl-c1">string</span>,
    <span class="pl-v">descriptor</span><span class="pl-k">:</span> <span class="pl-en">PropertyDescriptor</span>
) {
    <span class="pl-k"><span class="pl-k">const</span></span> originalMethod <span class="pl-k">=</span> <span class="pl-smi">descriptor</span>.<span class="pl-c1">value</span>;
    <span class="pl-smi">descriptor</span>.<span class="pl-c1">value</span> <span class="pl-k">=</span> <span class="pl-k">function</span>() {
      <span class="pl-c1">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">`</span>method ${<span class="pl-smi">propertyKey</span>} called<span class="pl-pds">`</span></span>);
      <span class="pl-k">return</span> <span class="pl-smi">originalMethod</span>.<span class="pl-c1">apply</span>(<span class="pl-c1">this</span>, <span class="pl-c1">arguments</span>);
    };
};
}
<p><span class="pl-k">class</span> <span class="pl-en">Test</span> {<br>
@<span class="pl-en">logFactory</span>(<span class="pl-s"><span class="pl-pds">"</span><span class="pl-pds">"</span></span>)<br>
<span class="pl-k">static</span> sayHello() {<br>
<span class="pl-c1">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">"</span>hello<span class="pl-pds">"</span></span>);<br>
}<br>
@<span class="pl-en">logFactory</span>(<span class="pl-s"><span class="pl-pds">"</span><span class="pl-pds">"</span></span>)<br>
<span class="pl-k">static</span> sum() {<br>
<span class="pl-k">return</span> <span class="pl-c1">1</span> <span class="pl-k">+</span> <span class="pl-c1">1</span>;<br>
}<br>
}</p>
<p><span class="pl-smi">Test</span>.<span class="pl-en">sayHello</span>();<br>
<span class="pl-smi">Test</span>.<span class="pl-en">sum</span>();</p></pre></div><p></p>
<p>执行结果:</p>
<div class="highlight highlight-source-shell"><pre> method sayHello called
hello
method sum called</pre></div>
<h2>多个装饰器</h2>
<p>多个装饰器可同时作用于同一对象,按顺序书写出需要运用的装饰器即可。其求值(evaluate)和真正被执行(call)的顺序是反向的。即,排在前面的先求值,排在最后的先执行。</p>
<p>譬如,</p>
<div class="highlight highlight-source-ts"><pre><span class="pl-k">function</span> f() {
<span class="pl-c1">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">"</span>f(): evaluated<span class="pl-pds">"</span></span>);
<span class="pl-k">return</span> <span class="pl-k">function</span>(<span class="pl-v">target</span>, <span class="pl-v">propertyKey</span><span class="pl-k">:</span> <span class="pl-c1">string</span>, <span class="pl-v">descriptor</span><span class="pl-k">:</span> <span class="pl-en">PropertyDescriptor</span>) {
    <span class="pl-c1">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">"</span>f(): called<span class="pl-pds">"</span></span>);
};
}
<p><span class="pl-k">function</span> g() {<br>
<span class="pl-c1">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">"</span>g(): evaluated<span class="pl-pds">"</span></span>);<br>
<span class="pl-k">return</span> <span class="pl-k">function</span>(<span class="pl-v">target</span>, <span class="pl-v">propertyKey</span><span class="pl-k">:</span> <span class="pl-c1">string</span>, <span class="pl-v">descriptor</span><span class="pl-k">:</span> <span class="pl-en">PropertyDescriptor</span>) {<br>
<span class="pl-c1">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">"</span>g(): called<span class="pl-pds">"</span></span>);<br>
};<br>
}</p>
<p><span class="pl-k">class</span> <span class="pl-en">C</span> {<br>
@<span class="pl-en">f</span>()<br>
@<span class="pl-en">g</span>()<br>
method() {}<br>
}</p></pre></div><p></p>
<p><strong>求值</strong> 的过程就体现在装饰器可能并不直接是一个可调用的函数,而是一个工厂方法或其他表达式,只有在这个工厂方法或表达式被求值后,才得到真正被调用的装饰器。</p>
<p>所以在这个示例中,先依次对 <code>f()</code> <code>g()</code> 求值,再从 <code>g()</code> 开始执行到 <code>f()</code>。</p>
<p>运行结果:</p>
<div class="highlight highlight-source-shell"><pre><span class="pl-en">f</span>(): evaluated
<span class="pl-en">g</span>(): evaluated
<span class="pl-en">g</span>(): called
<span class="pl-en">f</span>(): called</pre></div>
<h2>不同类型的装饰器</h2>
<h3>类的装饰器</h3>
<p>作用于类(Class)上的装饰器,用于修改类的一些属性。如果装饰器有返回值,该返回值将替换掉该类的声明而作为新的构造器使用。</p>
<p>装饰器入参:</p>
<ul>
<li>类的构造器。</li>
</ul>
<p>示例:</p>
<div class="highlight highlight-source-ts"><pre><span class="pl-k">function</span> sealed(<span class="pl-v">constructor</span><span class="pl-k">:</span> <span class="pl-en">Function</span>) {
<span class="pl-c1">Object</span>.<span class="pl-en">seal</span>(<span class="pl-smi">constructor</span>);
<span class="pl-c1">Object</span>.<span class="pl-en">seal</span>(<span class="pl-c1">constructor</span>.<span class="pl-c1">prototype</span>);
}
<p>@<span class="pl-smi">sealed</span><br>
<span class="pl-k">class</span> <span class="pl-en">Greeter</span> {<br>
greeting<span class="pl-k">:</span> <span class="pl-c1">string</span>;<br>
<span class="pl-k">constructor</span>(<span class="pl-v">message</span><span class="pl-k">:</span> <span class="pl-c1">string</span>) {<br>
<span class="pl-c1">this</span>.<span class="pl-smi">greeting</span> <span class="pl-k">=</span> <span class="pl-smi">message</span>;<br>
}<br>
greet() {<br>
<span class="pl-k">return</span> <span class="pl-s"><span class="pl-pds">"</span>Hello, <span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-c1">this</span>.<span class="pl-smi">greeting</span>;<br>
}<br>
}</p></pre></div><p></p>
<p><code>@sealed</code> 将类进行密封,将无法再向类添加属性,同时类上属性也变成不可配置的(non-configurable)。</p>
<p>另一个示例:</p>
<div class="highlight highlight-source-ts"><pre><span class="pl-k">function</span> classDecorator&lt;<span class="pl-en">T</span> <span class="pl-k">extends</span> { <span class="pl-k">new</span> (<span class="pl-k">...</span><span class="pl-v">args</span><span class="pl-k">:</span> <span class="pl-c1">any</span>[])<span class="pl-k">:</span> {} }&gt;(
<span class="pl-v">constructor</span><span class="pl-k">:</span> <span class="pl-en">T</span>
) {
<span class="pl-k">return</span> <span class="pl-k">class</span> <span class="pl-k">extends</span> <span class="pl-e">constructor</span> {
    newProperty <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">"</span>new property<span class="pl-pds">"</span></span>;
    hello <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">"</span>override<span class="pl-pds">"</span></span>;
};
}
<p>@<span class="pl-smi">classDecorator</span><br>
<span class="pl-k">class</span> <span class="pl-en">Greeter</span> {<br>
property <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">"</span>property<span class="pl-pds">"</span></span>;<br>
hello<span class="pl-k">:</span> <span class="pl-c1">string</span>;<br>
<span class="pl-k">constructor</span>(<span class="pl-v">m</span><span class="pl-k">:</span> <span class="pl-c1">string</span>) {<br>
<span class="pl-c1">this</span>.<span class="pl-smi">hello</span> <span class="pl-k">=</span> <span class="pl-smi">m</span>;<br>
}<br>
}</p>
<p><span class="pl-c1">console</span>.<span class="pl-c1">log</span>(<span class="pl-k">new</span> <span class="pl-en">Greeter</span>(<span class="pl-s"><span class="pl-pds">"</span>world<span class="pl-pds">"</span></span>));</p></pre></div><p></p>
<p>因为 <code>@classDecorator</code> 中有返回值,这个值将替换本来类的定义,当 <code>new</code> 的时候,使用的是装饰器中返回的构造器来创建类。</p>
<h3>方法的装饰器</h3>
<p>装饰器作用于类的方法时可用于观察,修改或替换该方法。如果装饰器有返回值,将替换掉被作用方法的属性描述器(roperty Descriptor)。</p>
<p>装饰器入参依次为:</p>
<ul>
<li>作用于静态方法时为类的构造器,实例方法时为类的原型(prototype)。</li>
<li>被作用的方法的名称。</li>
<li>被作用对象的属性描述器。</li>
</ul>
<p>示例:</p>
<div class="highlight highlight-source-ts"><pre><span class="pl-k">function</span> enumerable(<span class="pl-v">value</span><span class="pl-k">:</span> <span class="pl-c1">boolean</span>) {
<span class="pl-k">return</span> <span class="pl-k">function</span>(
    <span class="pl-v">target</span><span class="pl-k">:</span> <span class="pl-c1">any</span>,
    <span class="pl-v">propertyKey</span><span class="pl-k">:</span> <span class="pl-c1">string</span>,
    <span class="pl-v">descriptor</span><span class="pl-k">:</span> <span class="pl-en">PropertyDescriptor</span>
) {
    <span class="pl-smi">descriptor</span>.<span class="pl-smi">enumerable</span> <span class="pl-k">=</span> <span class="pl-smi">value</span>;
};
}
<p><span class="pl-k">class</span> <span class="pl-en">Greeter</span> {<br>
greeting<span class="pl-k">:</span> <span class="pl-c1">string</span>;<br>
<span class="pl-k">constructor</span>(<span class="pl-v">message</span><span class="pl-k">:</span> <span class="pl-c1">string</span>) {<br>
<span class="pl-c1">this</span>.<span class="pl-smi">greeting</span> <span class="pl-k">=</span> <span class="pl-smi">message</span>;<br>
}</p>
<p>@<span class="pl-en">enumerable</span>(<span class="pl-c1">false</span>)<br>
greet() {<br>
<span class="pl-k">return</span> <span class="pl-s"><span class="pl-pds">"</span>Hello, <span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-c1">this</span>.<span class="pl-smi">greeting</span>;<br>
}<br>
}</p></pre></div><p></p>
<p>上面示例中 <code>@enumerable</code> 改变了被装饰方法的 <code>enumerable</code> 属性,控制其是否可枚举。</p>
<p>类的方法可以是设置器(setter)或获取器(getter)。当两者成对出现时,应当只对其中一个运用装饰器,谁先出现就用在谁身上。因为装饰器应用时是用在 <code>get</code> 和 <code>set</code> 两者合并的属性描述器上的。</p>
<div class="highlight highlight-source-ts"><pre><span class="pl-k">class</span> <span class="pl-en">Test</span> {
<span class="pl-k">private</span> _foo <span class="pl-k">=</span> <span class="pl-c1">1</span>;
@<span class="pl-en">logFactory</span>(<span class="pl-s"><span class="pl-pds">"</span><span class="pl-pds">"</span></span>)
<span class="pl-k">get</span> foo() {
    <span class="pl-k">return</span> <span class="pl-c1">this</span>.<span class="pl-smi">_foo</span>;
}
<span class="pl-c"><span class="pl-c">//</span>🚨 Decorators cannot be applied to multiple get/set accessors of the same name.ts(1207)</span>
@<span class="pl-en">logFactory</span>(<span class="pl-s"><span class="pl-pds">"</span><span class="pl-pds">"</span></span>)
<span class="pl-k">set</span> foo(<span class="pl-v">val</span><span class="pl-k">:</span> <span class="pl-c1">number</span>) {
    <span class="pl-c1">this</span>.<span class="pl-smi">_foo</span> <span class="pl-k">=</span> <span class="pl-smi">val</span>;
}
}</pre></div>
<h3>属性的装饰器</h3>
<p>作用于类的属性时,其入参依次为:</p>
<ul>
<li>如果装饰的是静态属性则为类的构造器,实例属性则为类的原型</li>
<li>属性名</li>
</ul>
<p>此时并没有提供第三个入参,即该属性的属性描述器。因为定义属性时,没有相应机制来描述该属性,同时属性初始化时也没有方式可以对其进行修改或观察。</p>
<p>如果装饰器有返回值,将被忽略。</p>
<p>因此,属性装饰器仅可用于观察某个属性是否被创建。</p>
<p>一个示例:</p>
<div class="highlight highlight-source-ts"><pre><span class="pl-k">function</span> logProperty(<span class="pl-v">target</span><span class="pl-k">:</span> <span class="pl-c1">any</span>, <span class="pl-v">key</span><span class="pl-k">:</span> <span class="pl-c1">string</span>) {
<span class="pl-c"><span class="pl-c">//</span> property value</span>
<span class="pl-k">var</span> _val <span class="pl-k">=</span> <span class="pl-c1">this</span>[<span class="pl-smi">key</span>];
<p><span class="pl-c"><span class="pl-c">//</span> property getter</span><br>
<span class="pl-k">var</span> getter <span class="pl-k">=</span> <span class="pl-k">function</span>() {<br>
<span class="pl-c1">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds"><code>&lt;/span&gt;Get: ${&lt;span class="pl-smi"&gt;key&lt;/span&gt;} =&amp;gt; ${&lt;span class="pl-smi"&gt;_val&lt;/span&gt;}&lt;span class="pl-pds"&gt;</code></span></span>);<br>
<span class="pl-k">return</span> <span class="pl-smi">_val</span>;<br>
};</p>
<p><span class="pl-c"><span class="pl-c">//</span> property setter</span><br>
<span class="pl-k">var</span> setter <span class="pl-k">=</span> <span class="pl-k">function</span>(<span class="pl-v">newVal</span>) {<br>
<span class="pl-c1">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds"><code>&lt;/span&gt;Set: ${&lt;span class="pl-smi"&gt;key&lt;/span&gt;} =&amp;gt; ${&lt;span class="pl-smi"&gt;newVal&lt;/span&gt;}&lt;span class="pl-pds"&gt;</code></span></span>);<br>
<span class="pl-smi">_val</span> <span class="pl-k">=</span> <span class="pl-smi">newVal</span>;<br>
};</p>
<p><span class="pl-c"><span class="pl-c">//</span> Delete property.</span><br>
<span class="pl-k">if</span> (<span class="pl-k">delete</span> <span class="pl-c1">this</span>[<span class="pl-smi">key</span>]) {<br>
<span class="pl-c"><span class="pl-c">//</span> Create new property with getter and setter</span><br>
<span class="pl-c1">Object</span>.<span class="pl-en">defineProperty</span>(<span class="pl-smi">target</span>, <span class="pl-smi">key</span>, {<br>
get: <span class="pl-smi">getter</span>,<br>
set: <span class="pl-smi">setter</span>,<br>
enumerable: <span class="pl-c1">true</span>,<br>
configurable: <span class="pl-c1">true</span><br>
});<br>
}<br>
}</p>
<p><span class="pl-k">class</span> <span class="pl-en">Person</span> {<br>
@<span class="pl-smi">logProperty</span><br>
<span class="pl-k">public</span> name<span class="pl-k">:</span> <span class="pl-c1">string</span>;<br>
<span class="pl-k">public</span> surname<span class="pl-k">:</span> <span class="pl-c1">string</span>;</p>
<p><span class="pl-k">constructor</span>(<span class="pl-v">name</span><span class="pl-k">:</span> <span class="pl-c1">string</span>, <span class="pl-v">surname</span><span class="pl-k">:</span> <span class="pl-c1">string</span>) {<br>
<span class="pl-c1">this</span>.<span class="pl-c1">name</span> <span class="pl-k">=</span> <span class="pl-smi">name</span>;<br>
<span class="pl-c1">this</span>.<span class="pl-smi">surname</span> <span class="pl-k">=</span> <span class="pl-smi">surname</span>;<br>
}<br>
}</p>
<p><span class="pl-k">var</span> p <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">Person</span>(<span class="pl-s"><span class="pl-pds">"</span>remo<span class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>Jansen<span class="pl-pds">"</span></span>);<br>
<span class="pl-smi">p</span>.<span class="pl-c1">name</span> <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">"</span>Remo<span class="pl-pds">"</span></span>;<br>
<span class="pl-k">var</span> n <span class="pl-k">=</span> <span class="pl-smi">p</span>.<span class="pl-c1">name</span>;</p></pre></div><p></p>
<p>这个示例中,通过将原属性删除,创建带设置器和获取器的同名属性,来达到对属性值变化的监听。注意此时操作的已经不是最初那个属性了。</p>
<p>运行结果:</p>
<div class="highlight highlight-source-shell"><pre>Set: name =<span class="pl-k">&gt;</span> remo
Set: name =<span class="pl-k">&gt;</span> Remo
Get: name =<span class="pl-k">&gt;</span> Remo</pre></div>
<h3>参数的装饰器</h3>
<p>装饰器也可作用于方法的入参,这个方法不仅限于类的成员方法,还可以是类的构造器。装饰器的返回值会被忽略。</p>
<p>当作用于方法的参数时,装饰器的入参依次为:</p>
<ul>
<li>如果装饰的是静态方法则为类的构造器,实例方法则为类的原型。</li>
<li>被装饰的参数名。</li>
<li>参数在参数列表中的索引。</li>
</ul>
<p>比如,定义一个参数为必传的:</p>
<div class="highlight highlight-source-ts"><pre><span class="pl-k">import</span> <span class="pl-s"><span class="pl-pds">"</span>reflect-metadata<span class="pl-pds">"</span></span>;
<p><span class="pl-k"><span class="pl-k">const</span></span> requiredMetadataKey <span class="pl-k">=</span> <span class="pl-c1">Symbol</span>(<span class="pl-s"><span class="pl-pds">"</span>required<span class="pl-pds">"</span></span>);</p>
<p><span class="pl-k">function</span> required(<br>
<span class="pl-v">target</span><span class="pl-k">:</span> <span class="pl-en">Object</span>,<br>
<span class="pl-v">propertyKey</span><span class="pl-k">:</span> <span class="pl-c1">string</span> <span class="pl-k">|</span> <span class="pl-c1">symbol</span>,<br>
<span class="pl-v">parameterIndex</span><span class="pl-k">:</span> <span class="pl-c1">number</span><br>
) {<br>
<span class="pl-k">let</span> existingRequiredParameters<span class="pl-k">:</span> <span class="pl-c1">number</span>[] <span class="pl-k">=</span><br>
<span class="pl-c1">Reflect</span>.<span class="pl-en">getOwnMetadata</span>(<span class="pl-smi">requiredMetadataKey</span>, <span class="pl-smi">target</span>, <span class="pl-smi">propertyKey</span>) <span class="pl-k">||</span> [];<br>
<span class="pl-smi">existingRequiredParameters</span>.<span class="pl-c1">push</span>(<span class="pl-smi">parameterIndex</span>);<br>
<span class="pl-c1">Reflect</span>.<span class="pl-en">defineMetadata</span>(<br>
<span class="pl-smi">requiredMetadataKey</span>,<br>
<span class="pl-smi">existingRequiredParameters</span>,<br>
<span class="pl-smi">target</span>,<br>
<span class="pl-smi">propertyKey</span><br>
);<br>
}</p>
<p><span class="pl-k">function</span> validate(<br>
<span class="pl-v">target</span><span class="pl-k">:</span> <span class="pl-c1">any</span>,<br>
<span class="pl-v">propertyName</span><span class="pl-k">:</span> <span class="pl-c1">string</span>,<br>
<span class="pl-v">descriptor</span><span class="pl-k">:</span> <span class="pl-en">TypedPropertyDescriptor</span>&lt;<span class="pl-en">Function</span>&gt;<br>
) {<br>
<span class="pl-k">let</span> method <span class="pl-k">=</span> <span class="pl-smi">descriptor</span>.<span class="pl-c1">value</span>;<br>
<span class="pl-smi">descriptor</span>.<span class="pl-c1">value</span> <span class="pl-k">=</span> <span class="pl-k">function</span>() {<br>
<span class="pl-k">let</span> requiredParameters<span class="pl-k">:</span> <span class="pl-c1">number</span>[] <span class="pl-k">=</span> <span class="pl-c1">Reflect</span>.<span class="pl-en">getOwnMetadata</span>(<br>
<span class="pl-smi">requiredMetadataKey</span>,<br>
<span class="pl-smi">target</span>,<br>
<span class="pl-smi">propertyName</span><br>
);<br>
<span class="pl-k">if</span> (<span class="pl-smi">requiredParameters</span>) {<br>
<span class="pl-k">for</span> (<span class="pl-k">let</span> parameterIndex <span class="pl-k">of</span> <span class="pl-smi">requiredParameters</span>) {<br>
<span class="pl-k">if</span> (<br>
<span class="pl-smi">parameterIndex</span> <span class="pl-k">&gt;=</span> <span class="pl-c1">arguments</span>.<span class="pl-c1">length</span> <span class="pl-k">||</span><br>
<span class="pl-c1">arguments</span>[<span class="pl-smi">parameterIndex</span>] <span class="pl-k">===</span> <span class="pl-c1">undefined</span><br>
) {<br>
<span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">Error</span>(<span class="pl-s"><span class="pl-pds">"</span>Missing required argument.<span class="pl-pds">"</span></span>);<br>
}<br>
}<br>
}</p>
<pre><code>&lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-smi"&gt;method&lt;/span&gt;.&lt;span class="pl-c1"&gt;apply&lt;/span&gt;(&lt;span class="pl-c1"&gt;this&lt;/span&gt;, &lt;span class="pl-c1"&gt;arguments&lt;/span&gt;);
</code></pre>
<p>};<br>
}</p>
<p><span class="pl-k">class</span> <span class="pl-en">Greeter</span> {<br>
greeting<span class="pl-k">:</span> <span class="pl-c1">string</span>;</p>
<p><span class="pl-k">constructor</span>(<span class="pl-v">message</span><span class="pl-k">:</span> <span class="pl-c1">string</span>) {<br>
<span class="pl-c1">this</span>.<span class="pl-smi">greeting</span> <span class="pl-k">=</span> <span class="pl-smi">message</span>;<br>
}</p>
<p>@<span class="pl-smi">validate</span><br>
greet(@<span class="pl-smi">required</span> <span class="pl-v">name</span><span class="pl-k">:</span> <span class="pl-c1">string</span>) {<br>
<span class="pl-k">return</span> <span class="pl-s"><span class="pl-pds">"</span>Hello <span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-smi">name</span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">"</span>, <span class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-c1">this</span>.<span class="pl-smi">greeting</span>;<br>
}<br>
}</p></pre></div><p></p>
<p>上面示例中,<code>@required</code> 将参数标记为必需,配合 <code>@validate</code> 在调用真实的方法前进行检查。</p>
<h2>相关资源</h2>
<ul>
<li>TypeSdript Handbook - Decorators</li>
<li>GitHub Gist - decorators</li>
</ul>
      </td>
    </tr>
</tbody>
</table>

</div>
<div id="MySignature" role="contentinfo">
    <div>
<img src="https://licensebuttons.net/l/by-nc-sa/3.0/88x31.png" style="vertical-align: middle">
<strong>CC BY-NC-SA 署名-非商业性使用-相同方式共享</strong>
</div><br><br>
来源:https://www.cnblogs.com/Wayou/p/typescript_decorator.html
頁: [1]
查看完整版本: TypeScript 装饰器