如何实现打印机装饰?

TypeScript 1.5现在有装饰器 。

有人可以提供一个简单的例子,说明实现装饰器的正确方法,并描述可能有效的装饰器签名中的参数是什么意思?

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: Function, propertyKey: string | symbol, parameterIndex: number) => void; 

另外,在实现一个装饰器的时候,有没有最好的实践考虑因素呢?

我结束了与装饰者玩耍,并决定logging我想要的任何人谁想要利用这之前,任何文件出来。 如果您发现任何错误,请随时编辑。

一般要点

  • 当声明类时调用装饰器,而不是在实例化对象时调用装饰器。
  • 可以在同一个Class / Property / Method / Parameter上定义多个装饰器。
  • 装饰者不允许在构造函数上使用。

一个有效的装饰者应该是:

  1. 可分配给其中一个装饰器types( ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator )。
  2. 返回可分配给装饰值的值(在类装饰器和方法装饰器的情况下)。

参考


方法/正式Accessor装饰

执行参数:

  • target :类( Object )的原型。
  • propertyKey :方法的名称( string | symbol )。
  • descriptorTypedPropertyDescriptor – 如果您不熟悉描述符的键,我build议在Object.defineProperty (这是第三个参数)的文档中阅读它。

示例 – 没有参数

使用:

 class MyClass { @log myMethod(arg: string) { return "Message -- " + arg; } } 

执行:

 function log(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) { const originalMethod = descriptor.value; // save a reference to the original method // NOTE: Do not use arrow syntax here. Use a function expression in // order to use the correct value of `this` in this method (see notes below) descriptor.value = function(...args: any[]) { // pre console.log("The method args are: " + JSON.stringify(args)); // run and store result const result = originalMethod.apply(this, args); // post console.log("The return value is: " + result); // return the result of the original method (or modify it before returning) return result; }; return descriptor; } 

input:

 new MyClass().myMethod("testing"); 

输出:

方法参数是:[“testing”]

返回值是:消息 – testing

笔记:

  • 设置描述符的值时不要使用箭头语法。 如果你这样做的话,上下文不会是实例。
  • 通过返回一个新的描述符来修改原始描述符比覆盖当前描述符更好。 这允许你使用多个装饰器来编辑描述符,而不会覆盖另一个装饰器所做的事情。 这样做可以让你同时使用类似@enumerable(false)@log东西(Example: Bad vs Good )
  • 有用的TypedPropertyDescriptor的type参数可以用来限制修饰器可以放置哪些方法签名( 方法示例 )或访问器签名( Accessor Example )。

示例 – 使用参数(装饰工厂)

当使用参数时,你必须声明一个带有装饰器参数的函数,然后返回一个带有没有参数的例子的签名的函数。

 class MyClass { @enumerable(false) get prop() { return true; } } function enumerable(isEnumerable: boolean) { return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) => { descriptor.enumerable = isEnumerable; return descriptor; }; } 

静态方法装饰器

类似于装饰器的方法有一些不同之处:

  • 它的target参数是构造函数本身而不是原型。
  • 描述符是在构造函数中定义的,而不是原型。

类装饰

 @isTestable class MyClass {} 

执行参数:

  • target :装饰器声明的类( TFunction extends Function )。

使用示例 :使用元数据api将信息存储在类中。


属性装饰

 class MyClass { @serialize name: string; } 

执行参数:

  • target :类( Object )的原型。
  • propertyKeypropertyKey的名称( string | symbol )。

使用示例 :创build一个@serialize("serializedName")装饰器,并将属性名称添加到要序列化的属性列表中。


参数装饰器

 class MyClass { myMethod(@myDecorator myParameter: string) {} } 

执行参数:

  • target :类的原型( Function – 看起来Function不再起作用了,现在你应该使用any或者Object来使用任何类中的装饰器,或者指定要限制它的类的types至)
  • propertyKey :方法的名称( string | symbol )。
  • parameterIndex :函数参数列表中的参数索引( number )。

简单的例子

详细示例(s)

  • Memoize装饰器 – 方法,获取/设置访问器装饰器的例子
 class Foo { @consoleLogger Boo(name:string) { return "Hello, " + name } } 
  • 目标:在上述案例中,该类的原型是“Foo”
  • propertyKey:所调用方法的名称,在上面的例子中“Boo”
  • 描述符:描述对象=>包含值属性,这又是函数本身:function(name){return'Hello'+ name; }

您可以实现将每个调用logging到控制台的内容:

 function consoleLogger(target: Function, key:string, value:any) { return value: (...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('called method' + key + ' with args ' + a + ' returned result ' + r); return result; } } 

在其他答案中我没有看到一件重要的事情:

装饰工厂

如果我们想定制一个装饰器如何应用于一个声明,我们可以写一个装饰器工厂。 装饰器工厂只是一个函数,返回装饰器在运行时将调用的expression式。

 // This is a factory, returns one of ClassDecorator, // PropertyDecorator, MethodDecorator, ParameterDecorator function Entity(discriminator: string): { return function(target) { // this is the decorator, in this case ClassDecorator. } } @Entity("cust") export class MyCustomer { ... } 

检查TypeScript手册装饰者一章 。