如何实现typescript装饰器?
现在有了 有人能提供一个简单的例子来演示实现装饰器的正确方法,并描述可能的有效装饰器签名中的参数的含义吗如何实现typescript装饰器?,typescript,decorator,Typescript,Decorator,现在有了 有人能提供一个简单的例子来演示实现装饰器的正确方法,并描述可能的有效装饰器签名中的参数的含义吗 declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void; declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => voi
declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;
declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;
declare type ParameterDecorator = (target: Function, propertyKey: string | symbol, parameterIndex: number) => void;
declare-type-ClassDecorator=(target:TFunction)=>TFunction | void;
声明类型PropertyCorator=(目标:对象,propertyKey:string |符号)=>void;
声明类型MethodDecorator=(目标:对象,属性key:string |符号,描述符:TypedPropertyDescriptor)=>TypedPropertyDescriptor | void;
声明类型ParameterDecorator=(target:Function,propertyKey:string | symbol,parameterIndex:number)=>void;
此外,在实现decorator时,是否有任何最佳实践方面的考虑事项应该牢记在心
class Foo {
@consoleLogger
Boo(name:string) { return "Hello, " + name }
}
- 目标:在上面的例子中,类的原型是“Foo”
- propertyKey:在上述情况下,调用的方法的名称为“Boo”
- descriptor:description of object=>包含value属性,而value属性又是函数本身:函数(名称){return'Hello'+name;}
function consoleLogger(target: Function, key:string, value:any)
{
return value: (...args: any[]) =>
{
var a = args.map(a => JSON.stringify(a)).join();
var result = value.value.apply(this, args);
var r = JSON.stringify(result);
console.log('called method' + key + ' with args ' + a + ' returned result ' + r);
return result;
}
}
我最终和装饰师们玩了起来,并决定在任何文档出现之前,为任何想利用这一点的人记录我的想法。如果您发现任何错误,请随时编辑此内容 一般要点
- 在声明类时调用装饰器,而不是在实例化对象时调用装饰器
- 可以在同一个类/属性/方法/参数上定义多个装饰器
- 构造函数上不允许使用装饰符
ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator
)方法/形式访问器装饰器 实施参数:
:类的原型(target
)Object
:方法的名称(propertyKey
|)string
:A-如果您不熟悉描述符的键,我建议您阅读descriptor
(这是第三个参数)中的相关内容Object.defineProperty
function log(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) {
const originalMethod = descriptor.value; // save a reference to the original method
// NOTE: Do not use arrow syntax here. Use a function expression in
// order to use the correct value of `this` in this method (see notes below)
descriptor.value = function(...args: any[]) {
// pre
console.log("The method args are: " + JSON.stringify(args));
// run and store result
const result = originalMethod.apply(this, args);
// post
console.log("The return value is: " + result);
// return the result of the original method (or modify it before returning)
return result;
};
return descriptor;
}
输出:
方法参数为:[“测试”]
返回值为:Message--testing
注:
- 设置描述符的值时不要使用箭头语法李>
- 修改原始描述符比返回新描述符覆盖当前描述符更好。这允许您使用多个装饰器来编辑描述符,而不会覆盖另一个装饰器所做的工作。这样做允许您同时使用
和@enumerable(false)
之类的内容(例如:vs)@log
- 有用的:可以使用
的type参数来限制装饰器可以放置哪些方法签名()或访问器签名()TypedPropertyDescriptor
class MyClass {
@enumerable(false)
get prop() {
return true;
}
}
function enumerable(isEnumerable: boolean) {
return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) => {
descriptor.enumerable = isEnumerable;
return descriptor;
};
}
实现参数:
:声明装饰程序的类(target
)t函数扩展函数
房地产装饰师 实施参数:
:类的原型(target
)Object
:属性的名称(propertyKey
|)string
@serialize(“serializedName”)
装饰器,并将属性名称添加到要序列化的属性列表中
参数装饰器 实施参数:
:类的原型(target
-似乎Function
不再起作用了。您现在应该在此处使用Function
或any
,以便在任何类中使用装饰器。或者指定要限制它的类类型)Object
:方法的名称(propertyKey
|)string
:函数参数列表中的参数索引(parameterIndex
)number
- -方法,获取/设置访问器装饰器示例
- 在其他答案中,我没有看到一件重要的事情:
装饰厂
如果我们想定制如何将装饰器应用于声明,我们可以编写装饰器工厂。Decorator工厂只是一个函数,它返回Decorator将在运行时调用的表达式
查看TypeScript手册。TS装饰程序:
TS装饰器允许在类上添加额外的功能。在创建该类的任何实例之前,该类在声明时由装饰程序更改
语法:
装饰符是用
@
符号声明的,例如@metadata
。TS现在将搜索相应的元数据函数,并将自动为其提供七个参数,这些参数随具体修饰的内容而变化(例如,类或类属性获取不同的参数)
这些参数在decorator函数中提供:
- 类的原型对象
- propertykey或方法名
- PropertyDescriptor对象,如下所示
{writeable:true,enumerable:false,configurable:true,value:ƒ}
new MyClass().myMethod("testing");
class MyClass {
@enumerable(false)
get prop() {
return true;
}
}
function enumerable(isEnumerable: boolean) {
return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) => {
descriptor.enumerable = isEnumerable;
return descriptor;
};
}
@isTestable
class MyClass {}
class MyClass {
@serialize
name: string;
}
class MyClass {
myMethod(@myDecorator myParameter: string) {}
}
// This is a factory, returns one of ClassDecorator,
// PropertyDecorator, MethodDecorator, ParameterDecorator
function Entity(discriminator: string): {
return function(target) {
// this is the decorator, in this case ClassDecorator.
}
}
@Entity("cust")
export class MyCustomer { ... }
function overwrite(
target: myClass,
propertyKey: string,
descriptor: PropertyDescriptor
) {
console.log('I get logged when the class is declared!')
// desciptor.value refers to the actual function fo the class
// we are changing it to another function which straight up
// overrides the other function
descriptor.value = function () {
return 'newValue method overwritten'
}
}
function enhance(
target: myClass,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const oldFunc = descriptor.value;
// desciptor.value refers to the actual function fo the class
// we are changing it to another function which calls the old
// function and does some extra stuff
descriptor.value = function (...args: any[]) {
console.log('log before');
const returnValue = oldFunc.apply(this, args)
console.log('log after');
return returnValue;
}
}
class myClass {
// here is the decorator applied
@overwrite
foo() {
return 'oldValue';
}
// here is the decorator applied
@enhance
bar() {
return 'oldValueBar';
}
}
const instance =new myClass()
console.log(instance.foo())
console.log(instance.bar())
// The following gets logged in this order:
//I get logged when the class is declared!
// newValue method overwritten
// log before
// log after
// oldValueBar
function metaData(
target: myClass,
propertyKey: string,
// A Property Descriptor is not provided as an argument to a property decorator due to
// how property decorators are initialized in TypeScript.
) {
console.log('Execute your custom code here')
console.log(propertyKey)
}
class myClass {
@metaData
foo = 5
}
// The following gets logged in this order:
// Execute your custom code here
// foo
function seal(
constructor: Function,
) {
// Object.seal() does the following:
// Prevents the modification of attributes of
// existing properties, and prevents the addition
// of new properties
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@seal
class myClass {
bar?: any;
foo = 5
}
myClass.prototype.bar = 10;
// The following error will be thrown:
// Uncaught TypeError: Cannot add property bar,
// object is not extensible
// Returns a decorator function, we can return any function
// based on argument if we want
function decoratorFactory(arg: string) {
return function decorator(
target: myClass,
propertyKey: string,
) {
console.log(`Log arg ${arg} in decorator factory`);
}
}
// Define a decorator function directly
function decorator(
target: myClass,
propertyKey: string,
) {
console.log('Standard argument');
}
class myClass {
// Note the parentheses and optional arguments
// in the decorator factory
@decoratorFactory('myArgument')
foo = 'foo';
// No parentheses or arguments
@decorator
bar = 'bar';
}
// The following gets logged in this order:
// Log arg myArgument in decorator factory
// Standard argument
//Decorator function
function Prefixer(prefix: string) {
return function<T extends { new (...args: any[]): {name: string} }>(
originalCtor: T
) {
return class extends originalCtor {
constructor(..._: any[]) {
super();
this.name = `${prefix}.${this.name.toUpperCase()}`;
console.log(this.name);
}
};
};
}
@Prefixer('Mr')
class Person {
name = 'MBB';
constructor() {
console.log('original ctr logic here!');
}
}
const pers = new Person();
console.log(pers); //Mr.MBB