小编典典

如何实现打字稿装饰器?

all

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;

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


阅读 60

收藏
2022-06-13

共1个答案

小编典典

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

一般要点

  • 装饰器在类被声明时被调用,而不是在对象被实例化时被调用。
  • 可以在同一个类/属性/方法/参数上定义多个装饰器。
  • 构造函数上不允许使用装饰器。

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

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

参考


方法/形式访问器装饰器

实现参数:

  • target: 类的原型 ( Object)。
  • propertyKey: 方法的名称 ( string| symbol)。
  • descriptorTypedPropertyDescriptorA——如果您不熟悉描述符的键,我建议您在本文档中阅读它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;
}

输入:

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

输出:

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

返回值为:Message – testing

笔记:

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

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

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

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)。
  • propertyKey: 属性的名称 ( string| symbol)。

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


参数装饰器

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

实现参数:

  • target: 类的原型(Function“似乎Function不再起作用了。您现在应该在此处使用anyorObject以便在任何类中使用装饰器。或者指定要限制它的类类型)
  • propertyKey: 方法的名称 ( string| symbol)。
  • parameterIndex: 函数参数列表中参数的索引 ( number)。

简单的例子

详细示例

2022-06-13