Shallow in and shallow out Typescript Decorators

Keywords: Javascript Attribute TypeScript REST React

Temporary interest, want to write an article about ts decorator, spent half a day sorting out..
This stuff, in ES2017, seems to have... Documentation. Here.
Because temporarily, I don't want to write too many text introductions. Just open the code with a little text description.

This article explains what the decorator is through the decorator code compiled by ts. What can we do? What are the benefits?

Implementation code

The code after compilation is like this, with comments:

var __decorate =
  (this && this.__decorate) ||
  function(decorators, target, key, desc) {
    // c parameter length
    // R? C < 3 is the target, otherwise if desc is null, desc will take the description of the key attribute of the target, or else desc will be the desc
    // d Reserved for Use
    var c = arguments.length,
      r =
        c < 3
          ? target
          : desc === null
          ? (desc = Object.getOwnPropertyDescriptor(target, key))
          : desc,
      d;
    // The following text explains that this is just a throwing pot.
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
      r = Reflect.decorate(decorators, target, key, desc);
    // Loop decorators and assign values to d each time, and determine values
    else
      for (var i = decorators.length - 1; i >= 0; i--)
        if ((d = decorators[i]))
          // C < 3, using r as the input of decorators[i];
          // C > 3, target, key, r are executed as decorators[i];
          // C=== 3, target, key is executed as input to decorators[i].
          // If the execution does not return a result, r = r;.
          r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    // If C > 3 & & r, modify the target and return R
    return c > 3 && r && Object.defineProperty(target, key, r), r;
  };

As you can see from the code, the end result is either to use decorator to execute the target to change something; or to use Object.defineProperty to operate on the target, the code is just a few lines, which is of great use. The specific execution process will be easier to understand in combination with the following two examples.

It's worth mentioning that the Reflect in the code was supposed to be this sec-reflect-object But unfortunately not.

Then guess it's the implementation of Typescript, flip Typescript/tsc.js The code (see node_modules if you can't open the links) doesn't work either. Look at stackoverflow's explanation again. what-is-reflect-decorate-in-js-code-transpiled-from-ts

Generally speaking, ts wants to throw the pot to ES to make up for it. Then the code in ts's else is polyfill.

case

The following decorator s and class es are illustrated as examples

// ts code
function show(target: any) {
  console.log(target);
  target.prototype.showMe = (name: string) => {
    console.log("show me :", name);
  };
}

interface IShow {
  showMe?(name: string): any;
}

@show
class Show implements IShow {
  showMe(name: string) {}
}

const shoow = new Show();
shoow.showMe("ys");

// Compiled js
// Decrator, simple printing, and modifying methods
function show(target) {
  console.log(target);
  target.prototype.showMe = function(name) {
    console.log("show me :", name);
  };
}

// class Shoow
var Shoow = (function() {
  function Shoow() {}
  Shoow.prototype.showMe = function(name) {};
  // Decrators are [show], target is Shoow
  Shoow = __decorate([show], Shoow);
  return Shoow;
})();

var shooow = new Shoow();
shooow.showMe("ys");

// output : show me : ys

Understand the implementation steps:

  1. decorators = [show],target = Shoow,
  2. c = 2,r = target{Shoow},d = undefined
  3. There is no Reflect, go through the loop, only cycle once
  4. d = show, r = show(target{Shoow}), r does not return the result, so r is still r, r = target{Shoow}
  5. Return result: c = 2, so return false
  6. There is no return value after execution, but the showMe method is changed when the show(target{Shoow}) is executed, and the execution results are in line with expectations.

One is not enough? One more? Try returning a function in it this time?

// ts code
function logger1(config?) {
  return function(target, key: string, descriptor: PropertyDescriptor) {
    const _value = descriptor.value;
    if (typeof _value === "function") {
      descriptor.value = (...args) => {
        console.log(`logger1-begin : ${config.level}`);
        const res = _value.apply(target, args);
        console.log(`logger1-end`);
        return res;
      };
    }
    return descriptor;
  };
}

function logger2(config?) {
  return function(target, key: string, descriptor: PropertyDescriptor) {
    const _value = descriptor.value;
    if (typeof _value === "function") {
      descriptor.value = (...args) => {
        console.log(`logger2-begin : ${config.level}`);
        const res = _value.apply(target, args);
        console.log(`logger2-end`);
        return res;
      };
    }
    return descriptor;
  };
}

interface IShow {
  showMe?(name: string): any;
}

class Show implements IShow {
  @logger1({ level: "info" })
  @logger2({ level: "error" })
  showMe(name: string) {
    console.log("show me :", name);
  }
}

const shoow = new Show();
shoow.showMe("ys");

// The output is manually indented, and the showMe method has been wrapped several times.
// logger1-begin : info
//   logger2-begin : error
//     show me : ys
//   logger2-end
// logger1-end

Look again at the implementation steps:

  1. Decrators = logger1, logger2], target = Shoow, key = showMe, desc = null Note that this is for null, not undefined
  2. c = 4,r = target{Shoow},d = undefined
  3. There is no Reflect, go through the loop, only cycle once
  4. The first loop takes d = logger1, r = logger1(target, key, r), because return has a value, r = logger1's return function, then descriptor.value is rewritten for the first time.
  5. The second loop takes d = logger2, r = logger2(target, key, r), and because return has a value, r = logger2's return function, then descriptor.value is rewritten for the second time.
  6. Return result: Because C > 3, r has a value, Object.defineProperty(target, key, r) is executed to override the object property and return r (r is the result of rewriting).
  7. After two overrides of showMe attribute values, the execution results are in line with expectations.

Happy

What joy does the ornament bring you? Simply list some of the most obvious advantages, the rest in the use of their own extract the daily happiness to go to it.........................................

  1. Decoupling between business and functionality (such as logging)
  // daily
  dosomething(){
    consol.log('start')
    // some thing
    console.log('end')
  }

  // Use decorators
  @logger(logConfig?)
  dosomething();
  1. Clear code structure (especially for React components after multi-tier HOC)
  // Daily Multilayer HOC
  class MyComponent extends Component{
    // ..
  }
  connect(mapFn)(
    MyHoc(someMapFn)(
      Form.create(fieldsMapFn)(MyComponent)
    )
  )

  // Use decorators
  @connect(mapFn)
  @MyHoc(someMapFn)
  @FormFields(fieldsMapFn)
  class MyComponent extends Component{
    // ..
  }
  export default MyComponent;

Last

AOP, get to know

Posted by mad_hacker on Tue, 23 Apr 2019 14:00:33 -0700