TypeScript decorator learning

This is the 25th day of my participation in Gwen Challenge

Decorators can be used to decorate a class or properties within a class

Changes the behavior of a decorator target without modifying the source code in the decorator object

The test environment

The following practices are written in TS and run directly with TS-Node, requiring a global installation of typescipt and TS-Node

Global installation dependency

npm i typescript ts-node -g
Copy the code

Run ts file

ts-node xx.ts
Copy the code

Class decorator

The decorator object is a class

function classDecorator(target){
    return target
}

@classDecorator
class C{
    hello(){
        console.log('hello world')}}Copy the code
  • Target represents the target class for decoration

Example: Extend the sayHello method on the target class

function helloWorld(target){
    // target === Target class
    target.prototype.sayHello = function(){
        console.log('hello world');
    }
    return target
}

@helloWorld
class Router {
    sayHello() {
        throw new Error("Method not implemented."); }}const r = new Router()

r.sayHello() // hello world
Copy the code

Function decorator

The value of the decorator object is a function

function funDecorator(target, name, descriptor){
    return descriptor.value
}
class C{
    @funDecorator
    hello(){
        console.log('hello world')}}Copy the code
  • Target: Prototype of the target class
  • Name: the property name
  • Descriptor: attribute description symbol
    • Value: indicates the value of an attribute
    • Writable: Indicates whether it can be rewritten
    • Enumerable: Whether it is enumerable
    • Different: Indicates whether it can be configured

Example: a message indicating that a method has expired

function expired(target, name, descriptor) :any {
    console.log('fun:',name, 'is expired');
    return descriptor.value
}

@helloWorld
class Router {
    @expired
    hello() {
        / /... code
    }
    @expired
    hello2(){
        / /... code}}// fun: hello is expired
// fun: hello2 is expired
Copy the code

Get/set decorator

Decorator for a store/fetch

  • Target: Static methods point to class constructors and instance methods point to class prototypes
  • Name: the property name
  • Descriptor: attribute description symbol
    • Value: indicates the value of an attribute
    • Writable: Indicates whether it can be rewritten
    • Enumerable: Whether it is enumerable
    • Different: Indicates whether it can be configured
function getDecorator(target,name,descriptor){}function getDecorator(target,name,descriptor){}class C{
    private _x: number
    private _y: number
    constructor(x, y) {
        this._x = x
        this._y = y
    }

    @getDecorator
    get x() {
        return this._x
    }

    @getDecorator
    get y() {
        return this._y
    }
        
    @setDecorator
    set x(v) {
        this._x = v
    }

    @getDecorator
    set y(v) {
        this._y = v
    }
}
Copy the code

Emmmm, I don’t know where to put it

Attribute decorator

In short, the decorator object is a common property with the same arguments as the function decorator above

A function can also be thought of as an attribute of a class, except that the attribute value is function

function propertyDecorator(target,name){}class C{
    @propertyDecorator
    private property:string|undefined
}
Copy the code

Example: Set the default value

function defaultValue(v: any) {
    return function (target, name) {
        target[name] = v
    }
}

class Router {
    @defaultValue('666')
    public name: string | undefined
}


const r = new Router()

console.log(r.name);
Copy the code

The implementation of the decorator pass parameter can be seen as a closure call

You pass parameters when setting the decorator, and then return a function to actually decorate the target object

The decorated object can use the passed arguments

Function argument decorator

As the name implies, a decorator object is an argument in a function

function paramDecorator(target,name,idx){}class C{
    fun1(@paramDecorator a){}static func2(@paramDecorator a){}}Copy the code
  • Target: Static methods point to class constructors and instance methods point to class prototypes
  • Name: indicates the attribute name
  • Idx: The position of the argument in the function

Example: Get the relative positions of parameters in a function

function printParamPos(target, name, idx) {
    console.log('paramCount:', target[name].length, 'paramPos:', idx);
}

class Router {
    hello3(@printParamPos name: string) {
        console.log('hello3', name);
    }
    static hello4(@printParamPos name: string) {
        console.log('hello4', name); }}const r = new Router()
Router.hello4('123')
r.hello3('456')
// paramCount: 1 paramPos: 0
// paramCount: 1 paramPos: 0
// hello4 123
// hello3 456
Copy the code

The instance

I refer to the source code in Core-decorators

Deprecate API prompts

/** * disallow the specified API prompt *@param msg 
 * @returns * /
export function deprecate(msg = 'This function will be removed in future versions.') {
    return function (target, key, descriptor) {
        const methodSignature = `${target.constructor.name}#${key}`;
        return {
            ...descriptor,
            value: function () {
                console.warn(`DEPRECATION ${methodSignature}: ${msg}`);
                return descriptor.value.apply(this.arguments); }}; }}Copy the code

The test code

import { deprecate } from './decorators/index'

class TestClass {
    @deprecate(a)hello(name: string) {
        console.log('hello', name); }}const a = new TestClass()

a.hello('world')
Copy the code

Running results:

DEPRECATION TestClass#hello: This function will be removed in future versions.
hello world
Copy the code

reference

  • TypeScript-decorators
  • The core – decorators source code

To be continued

Take a look at the source code in Core-decorators and learn about good decorator design in common use