鸽了有一阵没输出东西,由于公司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
代码分析
- 定义了一个类型“Validator”,它是一个接受任意类型参数并返回布尔值的函数类型。
- 声明了一个变量 validateMap,它的类型为 Record<string, Validator[]> ,它是一个将函数名称映射到验证器数组的对象。 该对象将用于存储每个函数的验证器。
- 定义了一个函数“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相关,有兴趣的小伙伴可以讨论跟进。