Skip to content

TypeScript Decorators

ythy edited this page Apr 10, 2018 · 4 revisions

A Decorator is a special kind of declaration that can be attached to a class declaration, method, accessor, property, or parameter. Decorators use the form @expression, where expression must evaluate to a function that will be called at runtime with information about the decorated declaration. Decorators require ts1.5

For example, given the decorator @sealed we might write the sealed function as follows:

function sealed(target) {
    // do something with 'target' ...
}

Decorator Factories

If we want to customize how a decorator is applied to a declaration, we can write a decorator factory. A Decorator Factory is simply a function that returns the expression that will be called by the decorator at runtime.

We can write a decorator factory in the following fashion:

function color(value: string) { // this is the decorator factory
    return function (target) { // this is the decorator
        // do something with 'target' and 'value'...
    }
}

解析 Method decorators

Let’s create a method decorator named log

function log(target: Function, key: string, value: any) {
    return {
        value: function (...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(`Call: ${key}(${a}) => ${r}`);
            return result;
        }
    };
}

class C {
    @log
    foo(n: number) {
        return n * 2;
    }
}

A method decorators takes a 3 arguments:

  • target the method being decorated.
  • key the name of the method being decorated.
  • value a property descriptor of the given property if it exists on the object, undefined otherwise. The property descriptor is obtained by invoking the Object.getOwnPropertyDescriptor() function.

Without the @log decorator the generated JavaScript for the C class would just be as follows.

var C = (function () {
    function C() {
    }
    C.prototype.foo = function (n) {
        return n * 2;
    };
    return C;
})();

But when we add the @log decorator the following additional code is added to the class definition by the TypeScript compiler.

Object.defineProperty(
  __decorate(
    [log],                                              // decorators
    C.prototype,                                        // target
    "foo",                                              // key
    Object.getOwnPropertyDescriptor(C.prototype, "foo") // desc
  );
);

var C = (function () {
    function C() {
    }
    C.prototype.foo = function (n) {
        return n * 2;
    };
    Object.defineProperty(C.prototype, "foo",
        __decorate([
            log
        ], C.prototype, "foo", Object.getOwnPropertyDescriptor(C.prototype, "foo")));
    return C;
})();

The Object.defineProperty() method defines a new property directly on an object, or modifies an existing property on an object, and returns the object.

Property Decorator

Example:

function PropertyDecorator(
    target: Object, // The prototype of the class
    propertyKey: string | symbol // The name of the property
    ) {
    console.log("PropertyDecorator called on: ", target, propertyKey);
}

class PropertyDecoratorExample {
    @PropertyDecorator
    name: string;
}

console: PropertyDecorator called on: {} name

实例:

function method(server: string) { 
  return function(target: any, propertyKey: string) {
    target[propertyKey] = {
      server, method: getMethodName(propertyKey)
    }
  }
}

function formatUtilServlet(target: any, propertyKey: string) {
  target[propertyKey] = {
    server: ServerletKey.LBServletUtil, method: getMethodName(propertyKey)
  }
}

class ServletUtilKey {
  @formatUtilServlet static GetCurrentTimeMillis: UMethod;
}
class ServletUtilKey {
  @method(ServerletKey.LBServletUtil) static GetCurrentTimeMillis: UMethod;
}

Method Decorator

function MethodDecorator(
    target: Object, // The prototype of the class
    propertyKey: string, // The name of the method
    descriptor: TypedPropertyDescriptor<any>
    ) {
    console.log("MethodDecorator called on: ", target, propertyKey, descriptor);
}

class MethodDecoratorExample {
    @MethodDecorator
    method() {
    }
}
MethodDecorator called on:  { method: [Function] } method { value: [Function],
  writable: true,
  enumerable: true,
  configurable: true }

Class Decorator

No parameters:

function ClassDecorator(
    target: Function // The class the decorator is declared on
    ) {
    console.log("ClassDecorator called on: ", target);
}

@ClassDecorator
class ClassDecoratorExample {
}
ClassDecorator called on:  function ClassDecoratorExample() {
 }

With parameters:

function ClassDecoratorParams(param1: number, param2: string) {
    return function(
        target: Function // The class the decorator is declared on
        ) {
        console.log("ClassDecoratorParams(" + param1 + ", '" + param2 + "') called on: ", target);
    }
}

@ClassDecoratorParams(1, "a")
@ClassDecoratorParams(2, "b")
class ClassDecoratorParamsExample {
}
ClassDecoratorParams(2, 'b') called on:  function ClassDecoratorParamsExample() {
 }
ClassDecoratorParams(1, 'a') called on:  function ClassDecoratorParamsExample() {
 }

实例:

function initServer(server?: string) {
  return function(
    target: Function // The class the decorator is declared on
  ) {
    if (server)
      config.SERVER_URL = server;
    else
      config.SERVER_URL = `${util.getServerUrl()}servlet/`;
  }
}

Clone this wiki locally