typescript 如何实现打字稿装饰器?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29775830/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 06:37:55  来源:igfitidea点击:

How to implement a typescript decorator?

typescriptdecorator

提问by David Sherret

TypeScript 1.5now has decorators.

TypeScript 1.5现在有装饰器

Could someone provide a simple example demonstrating the proper way to implement a decorator and describe what the arguments in the possible valid decorator signatures mean?

有人可以提供一个简单的例子来展示实现装饰器的正确方法并描述可能的有效装饰器签名中的参数的含义吗?

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;

Additionally, are there any best practice considerations that should be kept in mind while implementing a decorator?

此外,在实现装饰器时是否应牢记任何最佳实践注意事项?

回答by David Sherret

I ended up playing around with decorators and decided to document what I figured out for anyone who wants to take advantage of this before any documentation comes out. Please feel free to edit this if you see any mistakes.

我最终玩弄了装饰器,并决定在任何文档出现之前为任何想要利用它的人记录我的想法。如果您发现任何错误,请随时编辑。

General Points

一般要点

  • Decorators are called when the class is declared—not when an object is instantiated.
  • Multiple decorators can be defined on the same Class/Property/Method/Parameter.
  • Decorators are not allowed on constructors.
  • 装饰器在类声明时调用——而不是在对象实例化时调用。
  • 可以在同一个类/属性/方法/参数上定义多个装饰器。
  • 构造函数上不允许使用装饰器。

A valid decorator should be:

  1. Assignable to one of the Decorator types (ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator).
  2. Return a value (in the case of class decorators and method decorator) that is assignable to the decorated value.

Reference

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

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

参考



Method / Formal Accessor Decorator

方法/正式访问器装饰器

Implementation parameters:

实现参数:

  • target: The prototype of the class (Object).
  • propertyKey: The name of the method (string| symbol).
  • descriptor: A TypedPropertyDescriptor— If you're unfamiliar with a descriptor's keys, I would recommend reading about it in this documentationon Object.defineProperty(it's the third parameter).
  • target: 类的原型 ( Object)。
  • propertyKey: 方法的名称 ( string| symbol)。
  • descriptor:一个TypedPropertyDescriptor-如果你不熟悉的描述符的钥匙,我会建议你阅读一下在这个文件Object.defineProperty(这是第三个参数)。

Example - Without Arguments

示例 - 不带参数

Use:

用:

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

Implementation:

执行:

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

Output:

输出:

The method args are: ["testing"]

The return value is: Message -- testing

方法参数是:[“测试”]

返回值为:Message——测试

Notes:

笔记:

  • Do not use arrow syntax when setting the descriptor's value. The context of thiswill not be the instance's if you do.
  • It's better to modify the original descriptor than overwriting the current one by returning a new descriptor. This allows you to use multiple decorators that edit the descriptor without overwriting what another decorator did. Doing this allows you to use something like @enumerable(false)and @logat the same time (Example: Badvs Good)
  • Useful: The type argument of TypedPropertyDescriptorcan be used to restrict what method signatures (Method Example) or accessor signatures (Accessor Example) the decorator can be put on.
  • 设置描述符的值时不要使用箭头语法。如果你这样做,上下文this将不是实例的。
  • 修改原始描述符比通过返回新描述符覆盖当前描述符更好。这允许您使用多个装饰器来编辑描述符,而不会覆盖另一个装饰器所做的工作。这样做可以让你使用像@enumerable(false)@log在同一时间(例如:VS
  • 有用: 的类型参数TypedPropertyDescriptor可用于限制可以放置装饰器的方法签名(方法示例)或访问器签名(访问器示例)。

Example - With Arguments (Decorator Factory)

示例 - 带参数(装饰工厂)

When using arguments, you must declare a function with the decorator's parameters then return a function with the signature of the example without arguments.

使用参数时,您必须声明一个带有装饰器参数的函数,然后返回一个带有不带参数示例签名的函数。

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


Static Method Decorator

静态方法装饰器

Similar to a method decorator with some differences:

类似于方法装饰器,但有一些区别:

  • Its targetparameter is the constructor function itself and not the prototype.
  • The descriptor is defined on the constructor function and not the prototype.
  • 它的target参数是构造函数本身而不是原型。
  • 描述符是在构造函数上定义的,而不是在原型上定义的。


Class Decorator

类装饰器

@isTestable
class MyClass {}

Implementation parameter:

实现参数:

  • target: The class the decorator is declared on (TFunction extends Function).
  • target: 装饰器在 ( TFunction extends Function)上声明的类。

Example use: Using the metadata api to store information on a class.

使用示例:使用元数据 api 存储类的信息。



Property Decorator

物业装饰师

class MyClass {
    @serialize
    name: string;
}

Implementation parameters:

实现参数:

  • target: The prototype of the class (Object).
  • propertyKey: The name of the property (string| symbol).
  • target: 类的原型 ( Object)。
  • propertyKey: 属性的名称 ( string| symbol)。

Example use: Creating a @serialize("serializedName")decorator and adding the property name to a list of properties to serialize.

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



Parameter Decorator

参数装饰器

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

Implementation parameters:

实现参数:

  • target: The prototype of the class (Function—it seems Functiondoesn't work anymore. You should use anyor Objecthere now in order to use the decorator within any class. Or specify the class type(s) you want to restrict it to)
  • propertyKey: The name of the method (string| symbol).
  • parameterIndex: The index of parameter in the list of the function's parameters (number).
  • target: 类的原型(Function- 它似乎Function不再起作用。您现在应该使用anyObjecthere 以便在任何类中使用装饰器。或者指定您想要将其限制为的类类型)
  • propertyKey: 方法的名称 ( string| symbol)。
  • parameterIndex: 函数参数列表中参数的索引 ( number)。

Simple example

简单的例子

Detailed Example(s)

详细示例

回答by Ondra ?i?ka

One important thing I don't see in the other answers:

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

Decorator factory

装饰厂

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.

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

// 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 { ... }

Check the TypeScript handbook Decorators chapter.

查看 TypeScript 手册装饰器章节

回答by Erik Lieben

class Foo {
  @consoleLogger 
  Boo(name:string) { return "Hello, " + name }
}
  • target: prototype of the class in the above case it's "Foo"
  • propertyKey: name of the method called, in the above case "Boo"
  • descriptor: description of object => contains value property, which in turn is the function itself: function(name) { return 'Hello' + name; }
  • 目标:上述情况下类的原型是“Foo”
  • propertyKey:被调用方法的名称,在上面的例子中为“Boo”
  • 描述符:对象的描述 => 包含 value 属性,而 value 属性又是函数本身: function(name) { return 'Hello' + name; }

You could implement something that logs each call to the console:

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

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