The complete set of typescript decorators

Decorators are mainly used in combination with classes

Decorators are divided into 1 type decorator, 2 attribute decorator, 3 method decorator, 4 method parameter decorator

Class decorator

Simple use of class decorators

// 类装饰器 params 指的是类本身,在浏览器中打印出来就是构造函数本身
function logClass (params: any) {
    
    
  console.log(params)
}

@logClass
class HttpClient {
    
    
  apiUrl:string;
  constructor(apiUrl:string) {
    
    
    this.apiUrl = apiUrl
  }
}

Add attributes and methods to the class decorator

// 装饰器
function logClass(params: any) {
    
    
  // 放到构造函数的原型上
  params.prototype.method = 'POST';
  params.prototype.setMethod = (value: 'POST' | 'GET') => {
    
    
    params.prototype.method = value;
  };
}
// 装饰器的使用
@logClass
class HttpClient {
    
    
  apiUrl: string;
  constructor(apiUrl: string) {
    
    
    this.apiUrl = apiUrl;
  }
}

const http1:any = new HttpClient('xxxx');

console.log(http1.apiUrl) // xxxx
console.log(http1.method) // POST
http1.setMethod('GET')
console.log(http1.method) // GET

Class decorator factory (can pass parameters to decorator functions)

The function returned by the logclassFunction function call is the real class decorator,
so logClassFunction is just a class decorator factory, used to produce class decorators (that is, the returned function)

// 装饰器工厂
function logClassFunction(slefArgus: {
    
     name: string; age: number }) {
    
    
  console.log(slefArgus);
  return (params: any) => {
    
    
    params.prototype.method = 'POST';
    params.prototype.setMethod = (value: 'POST' | 'GET') => {
    
    
      params.prototype.method = value;
    };
    params.prototype.data = slefArgus;
  };
}
// 装饰器工厂函数的调用方式
@logClassFunction({
    
     name: 'dx', age: 18 })
class HttpClient {
    
    
  apiUrl: string;
  constructor(apiUrl: string) {
    
    
    this.apiUrl = apiUrl;
  }
}

const http1: any = new HttpClient('xxxxx');

console.log(http1.apiUrl); // xxxx
console.log(http1.method); // POST
http1.setMethod('GET');
console.log(http1.method); // GET
console.log(http1.data); // {name: 'dx',age:18}

Class decorator override class

The class decorator can rewrite the class so that the properties and methods of the class itself will be replaced, but every necessary property and method must be rewritten, otherwise the type error will be reported

function reLogClass(targets: any) {
    
    
  // 返回一个新的class 扩展 targets (targets就是被重构的类)
  return class extends targets {
    
    
    apiUrl: string = '这是重构的url';
    getData() {
    
    
      return {
    
    
        url: this.apiUrl,
      };
    }
  };
}

@reLogClass
class HttpClient {
    
    
  apiUrl: string;
  constructor(apiUrl: string) {
    
    
    this.apiUrl = apiUrl;
  }
}

const http1: any = new HttpClient('xxxxx');

console.log(http1.apiUrl); //这是重构的url 
console.log(http1.getData()); // {url: '这是重构的url'}

Because the content in the HttpClient class is reconstructed by the decorator, the printed apiurl attribute is based on the class returned by the decorator function

Attribute decorator

The attribute decorator is used to modify the attributes in the class. Since it is to modify the attribute, you need to pass parameters. As we all know, you need to use a decorator factory. Unlike the class decorator factory, the attribute decorator factory returns a Property decorator only (all return a method).

function logProperty(params: string) {
    
    
  return (targets: any, attr: string) => {
    
    
    console.log(params, targets, attr);
    // targets是 constructor attr是属性的名称(apiUrl), params是传过来的参数(这是属性装饰器)
    // 这只能设置到原型上
    targets[attr] = params;
  };
}

class HttpClient {
    
    
  @logProperty('这是属性装饰器')
  apiUrl: string | undefined;
  constructor() {
    
    }
}

const http1 = new HttpClient();

console.log(http1.apiUrl); // 这是属性装饰器

Insert picture description here
According to my test, the attribute decorator can only set the default value, because through the parameter of the attribute decorator, only the value on the prototype can be set.
We all know that if there is no such value on the object instance, it will go back to the prototype to find it. If this value exists on the instance object, it will not look for it on the prototype (that is, the value of the prototype set by the decorator is invalid).

If the value of the instance exists

function logProperty(params: string) {
    
    
  return (targets: any, attr: string) => {
    
    
    console.log(params, targets, attr);
    // targets是 constructor attr是属性的名称(apiUrl), params是传过来的参数(这是属性装饰器)
    // 这只能设置到原型上
    targets[attr] = params;
  };
}

class HttpClient {
    
    
  @logProperty('这是属性装饰器')
  apiUrl: string | undefined = "xxxx";
  constructor() {
    
    }
}

const http1 = new HttpClient();

console.log(http1.apiUrl); // xxxx

Method decorator

Replace the decorated method

function logFunction(params: any) {
    
    
  return function (construct: any, functionName: string, functionDesc: any) {
    
    
    // construct 对于静态成员来说 是类的构造函数  对于实例成员是类的原型对象。
    // functionName 是方法的名字
    // functionDesc 是关于方法的一些描述 (一般不用,里面主要是是否可配置configurable 是否可数 enumerable 是否可写 writable 等)
    console.log(construct, functionName, functionDesc);
    // 将原先方法替换掉
    functionDesc.value = () => {
    
    
      return {
    
    
        relUrl: params,
      };
    };

    // 对原来的方法进行修改

  };
}

class HttpClient {
    
    
  apiUrl: string;
  constructor(apiUrl: string) {
    
    
    this.apiUrl = apiUrl;
  }
  @logFunction('xxxxx')
  getData() {
    
    
    return {
    
    
      url: this.apiUrl,
    };
  }
}

const http1 = new HttpClient('www.baidu.com');

console.log(http1.getData()) // {relUrl: "xxxxx"}

Modify the decorated method

function logFunction(params: any) {
    
    
  return function (construct: any, functionName: string, functionDesc: any) {
    
    
    // construct 对于静态成员来说 是类的构造函数  对于实例成员是类的原型对象。
    // functionName 是方法的名字
    // functionDesc 是关于方法的一些描述 (一般不用,里面主要是是否可配置configurable 是否可数 enumerable 是否可写 writable 等)
    console.log(construct, functionName, functionDesc);

    const oMethod = functionDesc.value;

    // 对原来的方法进行修改
    functionDesc.value = function (newUrl: string, method: string) {
    
    
      // 采用对象冒充,改变this,并将参数传入
      oMethod.call(this, newUrl, method);
      return {
    
    
        url: newUrl,
        method,
      };
    };
  };
}

class HttpClient {
    
    
  apiUrl: string;
  constructor(apiUrl: string) {
    
    
    this.apiUrl = apiUrl;
  }
  @logFunction('xxxxx')
  getData(newUrl: string, method: string) {
    
    
    console.log(`${
      
      newUrl}原先`);
  }
}

const http1 = new HttpClient('www.baidu.com');

console.log(http1.getData('www.taobao.com', 'get'));
// www.taobao.com原先
// {url: "www.taobao.com",method: "get"}
// tips: 从最终打印的结果可以发现,原先的方法也被执行了。

Method parameter decorator

The method parameter decorator is generally not used, and the functions it can achieve can also be achieved with other decorators.

function logAruguments(params: any) {
    
    
  return (target: any, methodName: string, paramsIndex: number) => {
    
    
    console.log(params, target, methodName, paramsIndex);
    // target 对于静态成员来说 是类的构造函数  对于实例成员是类的原型对象。
    // methodName 参数所在方法的名称
    // paramsIndex 参数所在方法参数的下标
    target.method = params;
  };
}

class HttpClient {
    
    
  apiUrl: string;
  method: string | undefined;
  constructor(apiUrl: string) {
    
    
    this.apiUrl = apiUrl;
  }
  getData(@logAruguments('post') method: string) {
    
    
    return {
    
    
      url: this.apiUrl,
      method: this.method || method,
    };
  }
}

const http1 = new HttpClient('xxxxx');

console.log(http1.getData('get'));
// {url: "xxxxx", method: "post"}
// 因为在方法参数装饰器中给this.method赋值了,所以打印出来不是get

Decorator execution order

In actual use, multiple decorators may be used together,

Description of the order of execution of decorators on the official website

The decorators on different declarations in the class will be applied in the order specified below:

Parameter decorator, then method decorator, accessor decorator, or attribute decorator is applied to each instance member.
Parameter decorator, then method decorator, accessor decorator, or attribute decorator is applied to each static member.
The parameter decorator is applied to the constructor.
The class decorator is applied to the class.

Parameter decorator>Method decorator>Method parameter decorator>Class decorator

If there are multiple decorators of each type, execute them in order from back to front and from right to left.

Guess you like

Origin blog.csdn.net/glorydx/article/details/112784468