TypeScript 装饰器
装饰器是一种特殊类型的声明,它可以附加到类声明、方法、属性或参数上,用来修改类的行为。
类装饰器
类装饰器在类声明之前被声明,应用于类构造函数,可以用来观察、修改或替换类定义。类装饰器可以叠加。
类装饰器示例
- 定义一个类装饰器 moveDecorator
- 装饰器接收目标类构造函数作为参数
- 通过修改原型对象为目标类添加新方法 getPosition
// 定义一个类装饰器
const moveDecorator: ClassDecorator = (target: Function) => {
target.prototype.getPosition = (): { x: number; y: number } => {
return { x: 100, y: 100 };
};
};
// 使用装饰器
@moveDecorator
class Tank {
public getPosition() {}
}
const t = new Tank();
console.log(t.getPosition());
类装饰器工厂
- 装饰器工厂是一个返回装饰器函数的函数
- 可以根据传入参数返回不同的装饰器实现
- 使装饰器更加灵活和可配置
const MusicDecoratorFactory = (type: string): ClassDecorator => {
switch (type) {
case 'Tank':
return (target: Function) => {
target.prototype.playMusic = () => {
console.log('坦克音乐');
};
};
case 'player':
return (target: Function) => {
target.prototype.playMusic = () => {
console.log('玩家音乐');
};
};
default:
return (target: Function) => {
target.prototype.playMusic = () => {
console.log('其他音乐');
};
};
}
};
@MusicDecoratorFactory('Tank')
class Tank {}
@MusicDecoratorFactory('player')
class player {}
方法装饰器
方法装饰器声明在一个方法声明之前,应用于方法的属性描述符,可以用来观察、修改或替换方法定义。
方法装饰器示例
- 方法装饰器接收三个参数:
- target: 目标类的原型对象
- propertyKey: 被装饰的方法名
- descriptor: 方法的属性描述符
- 可以通过修改 descriptor.value 来改变方法行为
const showDecoretor: MethodDecorator = (
arget: Object, // 目标类的原型对象
propertyKey: string | symbol, //被装饰的方法名
descriptor: TypedPropertyDescriptor<any> //被装饰方法的属性描述符
) => {
descriptor.value = '萧俊介';
};
class User {
@showDecoretor
public show() {
console.log('alrcly');
}
}
方法装饰器工厂
- 返回一个方法装饰器函数
- 可以根据传入参数定制装饰器行为
- 示例中实现了方法调用的延迟执行
const showDecoratorFactory =
(times: number): MethodDecorator =>
(arget: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<any>) => {
const method = descriptor.value;
descriptor.value = () => {
setTimeout(() => {
method();
}, times);
};
};
属性装饰器 和 参数装饰器
属性装饰器声明在一个属性声明之前,参数装饰器声明在一个参数声明之前。它们可以用来观察、修改或替换属性/参数的定义。
属性装饰器示例
- 属性装饰器接收两个参数:
- target: 目标类的原型对象
- propertyKey: 被装饰的属性名
- 可以用来观察或修改属性定义
// 属性装饰器
const PropDecorator: PropertyDecorator = (
target: Object, // 目标类的原型对象
propertyKey: string | symbol // 被装饰的属性的名称
) => {
console.log(propertyKey);
};
class HD {
@PropDecorator
public name: string | undefined;
}
参数装饰器示例
- 参数装饰器接收三个参数:
- target: 目标类的原型对象
- propertyKey: 被装饰的方法名(构造函数参数时为 undefined)
- parameterIndex: 参数在参数列表中的索引
- 可以用来观察或修改参数定义
const ParamsDecocrator: ParameterDecorator = (
target: Object, // 目标类的原型对象
propertyKey: string | symbol | undefined, // 被装饰的方法的名称(字符串或符号),如果装饰的是构造函数参数,则为 undefined
parameterIndex: number // 被装饰的参数在参数列表中的索引
) => {
console.log(parameterIndex);
};
class HD {
public show(id: number, @ParamsDecocrator content: string) {}
}
属性访问器动态转换对象属性
- 通过 Object.defineProperty 修改属性访问器
- 可以在 getter/setter 中添加自定义逻辑
- 示例实现了将属性值自动转换为小写
// 属性装饰器
const LowerDecorator: PropertyDecorator = (target: Object, propertyKey: string | symbol) => {
let value: string;
Object.defineProperty(target, propertyKey, {
get: () => {
return value.toLowerCase();
},
set: (v) => {
value = v;
},
});
};
//
class HD {
@LowerDecorator
public title: string | undefined;
}
const obj = new HD();
obj.title = 'ALrcLY';
console.log(obj.title);
元数据
reflect-metadata
是一个用于在 TypeScript
中支持反射元数据的库。它允许你在运行时访问和操作类、方法、属性等的元数据。元数据是附加到代码上的额外信息,通常用于描述代码的行为或配置。
在 TypeScript
中,reflect-metadata
通常与装饰器(Decorators)一起使用,以便在类、方法或属性上添加和读取元数据。这个库是 TypeScript
实现装饰器元数据功能的基础。
使用场景
- 依赖注入:在框架中自动注入依赖项。
- 序列化/反序列化:根据元数据自动处理对象的序列化和反序列化。
- ORM 映射:将类属性映射到数据库字段。
使用示例
import 'reflect-metadata';
class MyClass {
@Reflect.metadata('key', 'value')
myMethod() {
console.log('Hello, World!');
}
}
const metadataValue = Reflect.getMetadata('key', MyClass.prototype, 'myMethod');
console.log(metadataValue); // 输出:value
在这个例子中,@Reflect.metadata('key', 'value') 装饰器将元数据 'value' 附加到 myMethod 方法上,然后通过 Reflect.getMetadata 在运行时读取这个元数据。
注意事项
- reflect-metadata 需要在 TypeScript 项目中启用 emitDecoratorMetadata 选项。
- 这个库主要用于高级场景,普通应用开发中可能不需要直接使用它。
通过 reflect-metadata,你可以更灵活地控制和管理代码的元数据,从而实现更复杂的功能。
Reflect.defineMetadata
和 Reflect.getMetadata
是 TypeScript 中的元数据 API,通常用于在类、方法或属性上附加和获取元数据。这些元数据可以用于验证、依赖注入等场景。
使用 Reflect 的 defineMetadata 与 getMetadata 配置验证数据
import 'reflect-metadata';
const RequiredDecorator: ParameterDecorator = (
target: Object,
propertyKey: string | symbol | undefined,
parameterIndex: number
) => {
if (propertyKey === undefined) {
throw new Error('propertyKey cannot be undefined');
}
let requiredParams: number[] = Reflect.getMetadata('required', target, propertyKey) || [];
requiredParams.push(parameterIndex);
Reflect.defineMetadata('required', requiredParams, target, propertyKey);
};
const validateDecorator: MethodDecorator = (
target: Object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<any>
) => {
const method = descriptor.value;
descriptor.value = function () {
const requiredParams = Reflect.getMetadata('required', target, propertyKey) || [];
requiredParams.forEache((index: any) => {
if (index > arguments.length || arguments[index] === undefined) {
throw new Error('Error');
}
});
return method.apply(this, arguments);
};
};
class User {
@validateDecorator
find(name: string, @RequiredDecorator id: number) {
console.log(id);
}
}