首页 文章

如何实现打字稿装饰器?

提问于
浏览
174

TypeScript 1.5现在有decorators .

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

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;

另外,在实现装饰器时是否应该记住哪些最佳实践注意事项?

3 回答

  • 347
    class Foo {
      @consoleLogger 
      Boo(name:string) { return "Hello, " + name }
    }
    
    • target:上面这个类的原型是"Foo"

    • propertyKey:在上面的例子中调用的方法的名称"Boo"

    • 描述符:对象的描述=>包含值属性,而属性又是函数本身:function(name){return 'Hello' name; }

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

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

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

    装饰工厂

    如果我们想自定义装饰器如何应用于声明,我们可以编写一个装饰工厂 . Decorator Factory只是一个函数,它返回装饰器在运行时调用的表达式 .

    // 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手册Decorators chapter .

  • 4

    我最终玩装饰员,并决定记录我想出的任何想要在任何文档出来之前利用这个的人 . 如果您发现任何错误,请随时编辑此内容 .

    一般要点

    • 在声明类时调用装饰器 - 而不是在实例化对象时调用 .

    • 可以在同一个类/属性/方法/参数上定义多个装饰器 .

    • 构造函数不允许使用装饰器 .

    有效的装饰器应该是:可分配给其中一个Decorator类型(ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator) . 返回可赋值给装饰值的值(在类装饰器和方法装饰器的情况下) . 参考


    方法/正式访问者装饰器

    实施参数:

    • target :类的原型( Object ) .

    • propertyKey :方法的名称( string | symbol) .

    • descriptor :A TypedPropertyDescriptor - 如果你're unfamiliar with a descriptor'的键,我建议在 Object.definePropertythis documentation上阅读它(这是第三个参数) .

    示例 - 没有参数

    使用:

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

    输入:

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

    输出:

    方法args是:[“testing”]返回值是:消息 - 测试

    笔记:

    • 设置描述符的值时不要使用箭头语法 . The context of this will not be the instance's if you do.

    • 修改原始描述符比通过返回新描述符覆盖当前描述符更好 . 这允许您使用多个装饰器来编辑描述符而不会覆盖另一个装饰器所做的事情 . 这样做可以让你同时使用 @enumerable(false)@log 之类的东西(例如:Bad vs Good

    • UsefulTypedPropertyDescriptor 的类型参数可用于限制可以放置装饰器的方法签名(Method Example)或访问者签名(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 ) .

    Example use:使用元数据api存储类的信息 .


    Property Decorator

    class MyClass {
        @serialize
        name: string;
    }
    

    实施参数:

    • target :类的原型( Object ) .

    • propertyKey :属性的名称( string | symbol) .

    Example use:创建 @serialize("serializedName") 装饰器并将属性名称添加到要序列化的属性列表中 .


    参数装饰器

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

    实施参数:

    • target :类的原型( Function -it似乎 Function 不再起作用了 . 你现在应该使用 anyObject 以便在任何类中使用装饰器 . 或者指定你想要限制的类类型它)

    • propertyKey :方法的名称( string | symbol) .

    • parameterIndex :函数参数列表中的参数索引( number ) .

    Simple example

    详细示例

相关问题