current position:Home>Typescript decorator

Typescript decorator

2021-08-27 08:58:45 PiZriY

This is my participation 8 The fourth of the yuegengwen challenge 21 God , Check out the activity details :8 Yuegengwen challenge

Decorator

What is a decorator

Decorator -Decorators stay TypeScript Is a way to extend types by adding annotations without modifying class code

  • Reduce the amount of code
  • Improve code extensibility 、 Readability and maintainability

stay TypeScript in , Decorators can only be used in classes

Decorator syntax

The use of decorators is extremely simple

  • The decorator is essentially a function
  • The decorator function can be called at a specific location through a specific syntax ( class 、 Method 、 Even parameters, etc ) Expand

Enable decorator properties

  • experimentalDecorators: true
//  Decorator function 
function log(target: Function, type: string, descriptor: PropertyDescriptor) {
    let value = descriptor.value;

    descriptor.value = function(a: number, b: number) {
        let result = value(a, b);
        console.log(' journal :', {
            type,
            a,
            b,
            result
        })
        return result;
    }
}

//  Original class 
class M {
    @log
    static add(a: number, b: number) {
        return a + b;
    }
    @log
    static sub(a: number, b: number) {
        return a - b;
    }
}

let v1 = M.add(1, 2);
console.log(v1);
let v2 = M.sub(1, 2);
console.log(v2);
 Copy code 

Decorator

Decorator It's a function , It can go through @ Decorator function This special syntax is attached to class Method The accessor attribute Parameters On , Pack them , Then return a wrapped target object ( class Method The accessor attribute Parameters ), Decorators work in the construction phase of classes , Not the use phase

function  Decorator 1() {}
...

@ Decorator 1
class MyClass {
  
  @ Decorator 2
  a: number;
  
  @ Decorator 3
  static property1: number;
  
  @ Decorator 4
  get b() { 
    return 1; 
  }
  
  @ Decorator 5
  static get c() {
    return 2;
  }
  
  @ Decorator 6
  public method1(@ Decorator 5 x: number) {
    //
  }
  
  @ Decorator 7
  public static method2() {}
}
 Copy code 

Class decorator

The goal is

  • Constructors applied to classes

Parameters

  • The first parameter ( There's only one parameter )
    • Class as its only argument

Method decorator

The goal is

  • Apply to the methods of the class

Parameters

  • The first parameter
    • Static methods : Class constructor
    • Example method : Class
  • The second parameter
    • Method name
  • The third parameter
    • Method descriptor object

Attribute decorator

The goal is

  • Apply to the properties of the class

Parameters

  • The first parameter
    • Static methods : Class constructor
    • Example method : Class
  • The second parameter
    • The attribute name

Accessor decorator

The goal is

  • Accessors applied to classes (getter、setter) On

Parameters

  • The first parameter
    • Static methods : Class constructor
    • Example method : Class
  • The second parameter
    • The attribute name
  • The third parameter
    • Method descriptor object

Parameter decorator

The goal is

  • Apply to parameters

Parameters

  • The first parameter
    • Static methods : Class constructor
    • Example method : Class
  • The second parameter
    • Method name
  • The third parameter
    • The index of the parameter in the function parameter list

Decorator execution sequence

Instance decorator

​ attribute => The accessor => Parameters => Method

Static decorator

​ attribute => The accessor => Parameters => Method

class

​ class

Decorator factory

If we need to pass some parameters to the decorator during execution , You can use the decorator factory to achieve

//  Decorator function 
function log(callback: Function) {
  	return function(target: Function, type: string, descriptor: PropertyDescriptor) {
     	 	let value = descriptor.value;

        descriptor.value = function(a: number, b: number) {
            let result = value(a, b);
            callback({
                type,
                a,
                b,
                result
            });
            return result;
        }
    }
}

//  Original class 
class M {
    @log(function(result: any) {
      	console.log(' journal :', result)
    })
    static add(a: number, b: number) {
        return a + b;
    }
    @log(function(result: any) {
      	localStorage.setItem('log', JSON.stringify(result));
    })
    static sub(a: number, b: number) {
        return a - b;
    }
}

let v1 = M.add(1, 2);
console.log(v1);
let v2 = M.sub(1, 2);
console.log(v2);
 Copy code 

Metadata

stay Decorator Function , We can get class Method The accessor attribute Parameters Basic information of , Such as their names , The descriptor etc. , But if we want to get more information, we need to do it in another way : Metadata

What is metadata ?

Metadata : Data used to describe data , In our program , object class All these are data , They describe some kind of data , There is another kind of data , It can be used to describe object class , The data used to describe the data is Metadata

For example, a song itself is a set of data , There is also a group of singers who describe songs 、 Format 、 Long data , Then this group of data is the metadata of song data

Use reflect-metadata

www.npmjs.com/package/ref…

First , Need to install reflect-metadata

npm install reflect-metadata
 Copy code 

Define metadata

We can class Method And other data definition metadata

  • Metadata will be attached to the specified class Method Wait for the data , But it won't affect class Method Its own code

Set up

Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey)

  • metadataKey:meta Data key
  • metadataValue:meta Data value
  • target:meta Target of data attachment
  • propertyKey: Corresponding property key

Call mode

  • adopt Reflect.defineMetadata Method call to add Metadata

  • adopt @Reflect.metadata Decorator to add Metadata

import "reflect-metadata"

@Reflect.metadata("n", 1)
class A {
    @Reflect.metadata("n", 2)
    public static method1() {
    }
  
  	@Reflect.metadata("n", 4)
  	public method2() {
    }
}

// or
Reflect.defineMetadata('n', 1, A);
Reflect.defineMetadata('n', 2, A, 'method1');

let obj = new A();
Reflect.defineMetadata('n', 3, obj);
Reflect.defineMetadata('n', 4, obj, 'method2');

console.log(Reflect.getMetadata('n', A));
console.log(Reflect.getMetadata('n', A, ));
 Copy code 

obtain

Reflect.getMetadata(metadataKey, target, propertyKey)

The meaning of parameters and defineMetadata Corresponding

Using metadata log Decorator

import "reflect-metadata"

function L(type = 'log') {
  	return function(target: any) {
      	Reflect.defineMetadata("type", type, target);
    }
}
//  Decorator function 
function log(callback: Function) {
  	return function(target: any, name: string, descriptor: PropertyDescriptor) {
     	 	let value = descriptor.value;
      
      	let type = Reflect.getMetadata("type", target);

        descriptor.value = function(a: number, b: number) {
            let result = value(a, b);
          	if (type === 'log') {
              	console.log(' journal :', {
                  name,
                  a,
                  b,
                  result
                })
            }
          	if (type === 'storage') {
                localStorage.setItem('storageLog', JSON.stringify({
                  name,
                  a,
                  b,
                  result
                }));
            }
            return result;
        }
    }
}

//  Original class 
@L('log')
class M {
    @log
    static add(a: number, b: number) {
        return a + b;
    }
    @log
    static sub(a: number, b: number) {
        return a - b;
    }
}

let v1 = M.add(1, 2);
console.log(v1);
let v2 = M.sub(1, 2);
console.log(v2);
 Copy code 

Use emitDecoratorMetadata

stay tsconfig.json There is a configuration in emitDecoratorMetadata, Turn on this feature ,typescript It will be automatically given to... After compilation class Method The accessor attribute Parameters Add the following metadata

  • design:type: The type of object being decorated
    • Member attribute : The dimension type of the property
    • Member method :Function type
  • design:paramtypes
    • Member method : The annotation type of the method parameter list
    • class : Annotation type of constructor parameter list
  • design:returntype
    • Member method : The annotation type of the return value of the function
import "reflect-metadata"

function n(target: any) {
}
function f(name: string) {
    return function(target: any, propertyKey: string, descriptor: any) {
      	console.log( 'design type', Reflect.getMetadata('design:type', target, propertyKey) );
        console.log( 'params type', Reflect.getMetadata('design:paramtypes', target, propertyKey) );
        console.log( 'return type', Reflect.getMetadata('design:returntype', target, propertyKey) );
    }
}
function m(target: any, propertyKey: string) {

}

@n
class B {
    @m
    name: string;

    constructor(a: string) {

    }

    @f('')
    method1(a: string, b: string) {
        return 'a'
    }
}
 Copy code 

After compiling

__decorate([
    m,
    __metadata("design:type", String)
], B.prototype, "name", void 0);
__decorate([
    f(''),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, String]),
    __metadata("design:returntype", void 0)
], B.prototype, "method1", null);
B = __decorate([
    n,
    __metadata("design:paramtypes", [String])
], B);
 Copy code 

Conclusion

TypeScript The column is updated here for now , The new content learned later will continue to be updated , The summary is not comprehensive , You are also welcome to guide , If it feels good , give the thumbs-up + Collection ,

copyright notice
author[PiZriY],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2021/08/20210827085841322u.html

Random recommended