Decorators(装饰器) 是一种特殊的声明类型,可以使用在 类的声明,方法,访问器,属性和 参数 上;
装饰器的语法形式是:@express , 其中express必须计算为将在运行时调用的函数,其中包含有关装饰声明的信息;
类装饰器(Class Decorators)
类装饰器 放置在类定义之前, 应用到类的构造函数上, 可用于观察,修改和替换类的定义;
类装饰器在运行时作为函数被调用, 并且被装饰的类的构造函数是他唯一的参数
如果类装饰器的函数有返回值(新的构造函数), 那么 他 将用返回的构造函数 来替换原有的 类声明;
- 在
Demo的原型上新增了一个sayHello的方法
function demoDecorator<T extends { new (...args: any[]): {} }>(target: T) {
console.log(target);
}
@demoDecorator
class Demo {
type = 'demo';
constructor(private title: string) {}
}
// [class Demo]
- 返回值 构造函数发生了变化
function demoDecorator<T extends { new (...args: any[]): {} }>(target: T) {
console.log(target);
return class extends target {
constructor(...args: any[]) {
super(...args);
}
sayHello() {
console.log('Hello World');
}
};
}
@demoDecorator
class Demo {
type = 'demo';
constructor(public title: string) {}
}
console.log(Demo); // [class (anonymous) extends Demo]
但是 装饰器不会 更改 定义类的类型, 也就是 你新增的属性或者方法等, 在类型系统中都是未知的,也就是 提示错误
方法装饰器(Method Decorators)
方法装饰器放置在类的方法定义之前, 被应用于方法的属性描述符上,可用于观察,修改或者替换方法定义;
方法装饰器的表达式将在运行时作为函数调用,并带有以下三个参数:
- 静态成员的类的构造函数,或者实例成员的类的原型
- 成员名
- 成员属性描述符
function MethodDecorator(
target: any,
propKey: string,
descriptor: PropertyDescriptor,
) {
console.log(target, propKey, descriptor);
// {}
// sayHello
// {
// value: [Function: sayHello],
// writable: true,
// enumerable: false,
// configurable: true
// }
}
class Demo {
type = 'demo';
constructor(public title: string) {}
@MethodDecorator
sayHello() {
console.log('Hello World');
}
}
如果方法装饰器返回一个值,它将用作该方法的属性描述符。
function MethodDecorator(
target: any,
propKey: string,
descriptor: PropertyDescriptor,
) {
console.log(target, propKey, descriptor);
// {
// value: [Function: sayHello],
// writable: true,
// enumerable: false,
// configurable: true
// }
return {
enumerable: true,
value: function () {
console.log(12);
},
};
}
class Demo {
type = 'demo';
constructor(public title: string) {}
@MethodDecorator
sayHello() {
console.log('Hello World');
}
}
console.log(Object.getOwnPropertyDescriptor(Demo.prototype, 'sayHello'));
// {
// value: [Function: value], // valu值被修改了
// writable: true,
// enumerable: true, // enumerable 由 false 变成了 true
// configurable: true
// }
访问器装饰器(Accessor Decorators)
访问器装饰器在访问器声明之前声明。 访问器装饰器应用于访问器的属性描述符,可用于观察、修改或替换访问器的定义。
访问器装饰器的表达式将在运行时作为函数调用,并带有以下三个参数(跟方法装饰器一样):
- 静态成员的类的构造函数,或者实例成员的类的原型
- 成员名
- 成员属性描述符
如果访问器装饰器返回一个值,它将用作该方法的属性描述符。 注意:不能向多个同名的 get/set 访问器应用修饰器
unction AccessorDecorator(
target: any,
propKey: string,
descriptor: PropertyDescriptor,
) {
descriptor.configurable = false;
}
class Demo {
constructor(private _name: string) {}
@AccessorDecorator
get name(): string {
return this._name;
}
@AccessorDecorator
set name(str: string) {
this._name = str;
}
}
属性装饰器(Property Decorators)
属性装饰器在属性声明之前声明.
属性装饰器的表达式将在运行时作为函数调用,并带有以下两个参数:
- 静态成员的类的构造函数,或者实例成员的类的原型
- 属性名
注意 由于属性装饰器在 TypeScript 中的初始化方式,属性描述符不作为参数提供给属性装饰器。 这是因为目前在定义原型成员时没有描述实例属性的机制,也没有办法观察或修改属性的初始值设定项。 返回值也被忽略。 因此,属性装饰器只能用于观察已为类声明了特定名称的属性。
下面的例子就是格式化日期:
import 'reflect-metadata';
import * as dayjs from 'dayjs';
const formateMetaDataKey = Symbol('format');
function format(formateString: string) {
return Reflect.metadata(formateMetaDataKey, formateString);
}
function getFormat(target: any, propKey: string): string {
const formateString: string = Reflect.getMetadata(
formateMetaDataKey,
target,
propKey,
);
return dayjs(target[propKey]).format(formateString);
}
class Demo {
@format('YYYY-MM-DD')
date: Date;
getDate(): string {
const str = getFormat(this, 'date');
return str;
}
}
const d: Demo = new Demo();
d.date = new Date('2022-03-04 15:55:00');
console.log(d.getDate()); // 2022-03-04
参数装饰器(Parameter Decorators)
参数装饰器在参数声明之前声明.
参数装饰器的表达式将在运行时作为函数调用,并带有以下三个参数:
- 静态成员的类的构造函数,或者实例成员的类的原型
- 属性名
- 参数在函数参数列表的位置
function required(target: any, propKey: string, index: number) {
console.log(target, propKey, index);
// {} print 1
// {} print 0
}
class Demo {
print(@required str: string, @required suffix: string) {
console.log(str + suffix);
}
}
判断元素是否为空
import 'reflect-metadata';
const requiredMetadataKey = Symbol('required');
function required(target: any, propKey: string, index: number) {
const existingRequiredParameters: number[] =
Reflect.getOwnMetadata(requiredMetadataKey, target, propKey) || [];
existingRequiredParameters.push(index);
Reflect.defineMetadata(
requiredMetadataKey,
existingRequiredParameters,
target,
propKey,
);
}
function validate(
target: any,
propKey: string,
descriptor: PropertyDescriptor,
) {
const method = descriptor.value;
descriptor.value = function (...args: string[]) {
const requiredParameters: number[] = Reflect.getOwnMetadata(
requiredMetadataKey,
target,
propKey,
);
console.log(requiredParameters);
if (requiredParameters) {
for (const index of requiredParameters) {
if (index >= args.length || args[index] === undefined) {
throw new Error('参数不能为空');
}
}
return method.apply(this, args);
}
};
}
class Demo {
@validate
print(xixi: string, @required str: string, @required suffix?: string) {
console.log(xixi + str + suffix);
}
}
new Demo().print('xixi', '12');