参数装饰器在参数声明之前声明。参数装饰器应用于类构造函数或方法声明的函数。
参数装饰器接收三个参数:
target: Object - 被装饰的类
propertyKey: string | symbol - 方法名
parameterIndex: number - 方法中参数的索引值
参数装饰器通常与方法装饰器组合使用实现一些特殊功能。
1、不带参数的参数装饰器
以下代码演示使用参数装饰器对方法输入参数进行非空校验:
export class User {
@validate
say(@required sth: string) {
console.log(`user say: ${sth}`);
}
}
//组件中调用
let user = new User();
user.say("");
user.say("hi");
运行打印:
可见程序对添加了 required 装饰器的参数进行了非空值的校验,当传入参数为空字符串时输出了报错信息。这里为了实现校验功能,添加了一个校验工具类:
export class Validator {
private static requiredMap: Map<any, Map<string, any>> = new Map();
static registerRequired(target: any, methodName: string, paramIndex: number) {
let targetMap = this.requiredMap.get(target);
if (!targetMap) {
targetMap = new Map();
this.requiredMap.set(target, targetMap);
}
let methodMap = targetMap.get(methodName);
if (!methodMap) {
methodMap = new Map();
targetMap.set(methodName, methodMap);
}
methodMap.set(paramIndex, true);
}
static validateRequired(target: any, methodName: string, paramValues: any[]): boolean {
let targetMap = this.requiredMap.get(target);
if (!targetMap) return true;
let methodMap = targetMap.get(methodName);
if (!methodMap) return true;
for (const [index, paramValue] of paramValues.entries()) {
let required = methodMap.get(index);
if (!!required) {
if (typeof paramValue === 'undefined' || paramValue === null || paramValue === "") {
console.error(`param at index ${index} for method ${methodName} must have non-null value.`);
return false;
}
}
}
return true;
}
}
其中 registerRequired 方法用于注册需校验方法参数信息,validateRequired 方法用于校验方法参数。
在 required 参数装饰器的代码中进行校验信息注册:
function required(target: any, propertyKey: string, parameterIndex: number) {
Validator.registerRequired(target, propertyKey, parameterIndex);
}
在 validate 方法装饰器的代码中调用校验方法:
function validate(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
let originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
if (!Validator.validateRequired(target, propertyKey, args)) {
return;
}
return originalMethod.apply(this, args);
}
}
2、带参数的参数装饰器
当然,参数装饰器也允许传递参数,修改以上代码,满足方法参数正则表达式的校验。
修改后的User类:
export class User {
name: string = "";
mail: string = "";
@validate
say(@required sth: string) {
console.log(`user say: ${sth}`);
}
@validate
setInfo(@reg('^[A-Za-z0-9]+$') name: string, @reg('^[A-Za-z0-9]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$') mail: string) {
this.name = name;
this.mail = mail;
console.log(`name: ${this.name}, mail: ${this.mail}`);
}
}
setInfo 方法限定 name 只能输入英文字母和数字,限定 mail 必须满足邮箱格式。
修改校验工具类,添加针对正则表达式的注册和校验方法:
private static regMap: Map<any, Map<string, any>> = new Map();
static registerReg(target: any, methodName: string, paramIndex: number, pattern: string) {
let targetMap = this.regMap.get(target);
if (!targetMap) {
targetMap = new Map();
this.regMap.set(target, targetMap);
}
let methodMap = targetMap.get(methodName);
if (!methodMap) {
methodMap = new Map();
targetMap.set(methodName, methodMap);
}
methodMap.set(paramIndex, pattern);
}
static validateReg(target: any, methodName: string, paramValues: any[]): boolean {
let targetMap = this.regMap.get(target);
if (!targetMap) return true;
let methodMap = targetMap.get(methodName);
if (!methodMap) return true;
for (const [index, paramValue] of paramValues.entries()) {
let pattern = methodMap.get(index);
if (!!pattern) {
let pass = new RegExp(pattern).test(paramValue);
if (!pass) {
console.error(`param at index ${index} for method ${methodName} does not match regular expression.\nwanted: ${pattern}\npassed value: ${paramValue}`);
return false;
}
}
}
return true;
}
reg 参数装饰器代码:
function reg(pattern: string) {
return function (target: any, propertyKey: string, parameterIndex: number) {
Validator.registerReg(target, propertyKey, parameterIndex, pattern);
}
}
修改validate方法装饰器的代码,添加正则表达式的校验:
function validate(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
let originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
if (!Validator.validateRequired(target, propertyKey, args)) {
return;
}
if (!Validator.validateReg(target, propertyKey, args)) {
return;
}
return originalMethod.apply(this, args);
}
}
组件中调用代码:
let user = new User();
user.say("");
user.say("hi");
user.setInfo("..", "dd");
user.setInfo("abc1", "dd");
user.setInfo("1", "1@1.com");
运行打印:
可见,指定了正则表达式的参数装饰器结合方法装饰器的确对方法的参数进行了正则表达式的匹配校验,不符合匹配规则的参数没有进行方法的调用。
3、多个参数装饰器装饰一个参数
当然,同一个参数支持添加多个装饰器,修改 say 方法对参数同时添加 required 和 reg 装饰器,reg 装饰器限制不能传入包含 shit 的字符串,相当于违禁字符串,修改代码如下:
@validate
say(@required @reg('^(?!.*?shit).*$') sth: string) {
console.log(`user say: ${sth}`);
}
组件中调用代码:
let user = new User();
user.say("");
user.say("shit");
user.say("hi");
运行打印:
可见,达到了期望的效果。
分析上述程序运行情况可以知道,参数装饰器的代码在编译时被执行,而方法装饰器的代码在每次方法被调用都会被执行。
参数装饰器的使用就介绍到这里,欢迎意见交流。