Typescript中的装饰器(一)

85 阅读3分钟

鸽了有一阵没输出东西,由于公司6.30之前需要达成一个里程碑,前一阵忙于gis地图与cad图的公共组件库封装的需求以及适配到各个子系统之中。

偶然间发现掘友发了一篇关于通过装饰器来实现按钮权限的控制,正好最近有时间,写篇相关的分享,话不多说进入正题。(tips:关于装饰器的基本认知如果不清楚请先查阅相关文档)

先上代码

type Validator = (x: any) => boolean;

// save the marks
const validateMap: Record<string, Validator[]> = {};

// 1. mark the parameters need to be validated
function typedDecoratorFactory(validator: Validator): ParameterDecorator {
  return (_, key, index) => {
    const target = validateMap[key as string] ?? [];
    target[index] = validator;
    validateMap[key as string] = target;
  }
}

function validate(_: Object, key: string, descriptor: PropertyDescriptor) {
  const originalFn = descriptor.value;
  descriptor.value = function(...args: any[]) {

    // 2. run the validators
    const validatorList = validateMap[key];
    if (validatorList) {
      args.forEach((arg, index) => {
        const validator = validatorList[index];

        if (!validator) return;

        const result = validator(arg);

        if (!result) {
          throw new Error(
            `Failed for parameter: ${arg} of the index: ${index}`
          );
        }
      });
    }

    // 3. run the original method
    return originalFn.call(this, ...args);
  }
}

const isInt = typedDecoratorFactory((x) => Number.isInteger(x));
const isString = typedDecoratorFactory((x) => typeof x === 'string');

class C {
  @validate
  sayRepeat(@isString word: string, @isInt x: number) {
    return Array(x).fill(word).join(',');
  }
}

const c = new C();
const result = c.sayRepeat('hello', 2); // pass
console.log(result)

再说结果,控制台打印结果:hello,hello

代码分析

  1. 定义了一个类型“Validator”,它是一个接受任意类型参数并返回布尔值的函数类型。
  2. 声明了一个变量 validateMap,它的类型为 Record<string, Validator[]> ,它是一个将函数名称映射到验证器数组的对象。 该对象将用于存储每个函数的验证器。
  3. 定义了一个函数“typedDecoratorFactory”,它接受一个验证器作为参数并返回一个“ParameterDecorator”函数。 返回的函数将用作函数参数的装饰器。

typedDecoratorFactory 函数接受 validator 参数并返回 ParameterDecorator 函数。 该装饰器函数采用三个参数:“_”(被忽略)、“key”(函数名称)和“index”(被装饰参数的索引)。
在装饰器函数内,它从“validateMap”对象中检索该函数的验证器数组。 如果该数组不存在,则创建一个空数组。
然后将“validator”分配给数组中相应的索引。
使用更新后的验证器数组更新“validateMap”对象。

4.定义了一个函数“validate”,它接受三个参数:“_”(忽略)、“key”(函数名称)和“descriptor”(函数的属性描述符)。

在“validate”函数内,它将原始函数分配给“originalFn”变量。
然后,它使用带有任意数量参数的新函数覆盖原始函数。
在新函数内,它从“validateMap”对象中检索该函数的验证器数组。
如果数组存在,它将迭代参数及其相应的验证器。
如果参数存在验证器,则它通过使用参数调用验证器函数来验证参数。
如果验证失败(验证器返回 false ),它会抛出一个错误,指示失败的参数及其索引
验证所有参数后,它使用“call”方法调用原始函数,传入当前对象(“this”)和参数。

5.通过“typedDecoratorFactory”函数定义了两个装饰器“isInt”和“isString”。 这些装饰器用于验证函数参数的类型。
6.定义了一个类“C”,其方法“sayRepeat”用“validate”装饰器来装饰,其参数用“isString”和“isInt”装饰器来装饰。
7.sayRepeat方法接受一个string类型的word参数和一个number类型的x参数。 它返回一个重复“word”“x”次数的字符串。


ok,代码梳理到这里,各位应该对装饰器已经有了一定的了解,后续将更新reflect以及Metadata相关,有兴趣的小伙伴可以讨论跟进。