codingleo
11/17/2015 - 3:28 AM

TypeScript Decorators Examples

TypeScript Decorators Examples

function logClass(target: any) {
 
  // save a reference to the original constructor
  var original = target;
 
  // a utility function to generate instances of a class
  function construct(constructor, args) {
    var c : any = function () {
      return constructor.apply(this, args);
    }
    c.prototype = constructor.prototype;
    return new c();
  }
 
  // the new constructor behaviour
  var f : any = function (...args) {
    console.log("New: " + original.name);
    return construct(original, args);
  }
 
  // copy prototype so intanceof operator still works
  f.prototype = original.prototype;
 
  // return new constructor (will override original)
  return f;
}

@logClass
class Person { 

  public name: string;
  public surname: string;

  constructor(name : string, surname : string) { 
    this.name = name;
    this.surname = surname;
  }
}

var p = new Person("remo", "jansen");
@logClassWithArgs({ when : { name : "remo"} })
class Person {
  public name: string;
 
  // ...
}

function logClassWithArgs(filter: Object) {
    return (target: Object) => {
        // implement class decorator here, the class decorator
        // will have access to the decorator arguments (filter)
        // because they are  stored in a closure
    }
}
function log(...args : any[]) {
  switch(args.length) {
    case 1:
      return logClass.apply(this, args);
    case 2:
      return logProperty.apply(this, args);
    case 3:
      if(typeof args[2] === "number") {
        return logParameter.apply(this, args);
      }
      return logMethod.apply(this, args);
    default:
      throw new Error();
  }
}
function logMethod(target, key, descriptor) {
 
    // save a reference to the original method this way we keep the values currently in the
    // descriptor and don't overwrite what another decorator might have done to the descriptor.
    if(descriptor === undefined) {
      descriptor = Object.getOwnPropertyDescriptor(target, key);
    }
    var originalMethod = descriptor.value;
 
    //editing the descriptor/value parameter
    descriptor.value = function () {
        var args = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            args[_i - 0] = arguments[_i];
        }
        var a = args.map(function (a) { return JSON.stringify(a); }).join();
        // note usage of originalMethod here
        var result = originalMethod.apply(this, args);
        var r = JSON.stringify(result);
        console.log("Call: " + key + "(" + a + ") => " + r);
        return result;
    };
 
    // return edited descriptor as opposed to overwriting the descriptor
    return descriptor;
}

class Person { 
  
  public name: string;
  public surname: string;
  
  constructor(name : string, surname : string) { 
    this.name = name;
    this.surname = surname;
  }
  
  @logMethod
  public saySomething(something : string, somethingElse : string) : string { 
    return this.name + " " + this.surname + " says: " + something + " " + somethingElse; 
  }
}

var p = new Person("remo", "jansen");
p.saySomething("I love playing", "halo");
function logParameter(target: any, key : string, index : number) {
  var metadataKey = `__log_${key}_parameters`;
  if (Array.isArray(target[metadataKey])) {
    target[metadataKey].push(index);
  }
  else {
    target[metadataKey] = [index];
  }
}

function logMethod(target, key, descriptor) {
	
    if(descriptor === undefined) {
      descriptor = Object.getOwnPropertyDescriptor(target, key);
    }
    var originalMethod = descriptor.value;

    //editing the descriptor/value parameter
	  descriptor.value = function (...args: any[]) {

    	var metadataKey = `__log_${key}_parameters`;
    	var indices = target[metadataKey];

  		if (Array.isArray(indices)) { 
  			for (var i = 0; i < args.length; i++) { 
  		
  				if (indices.indexOf(i) !== -1) { 
  		
  				var arg = args[i];
  				var argStr = JSON.stringify(arg) || arg.toString();
  				console.log(`${key} arg[${i}]: ${argStr}`);
  				}
  			}
  			var result = originalMethod.apply(this, args);
  			return result;
  		}
  		else {
  			var a = args.map(a => (JSON.stringify(a) || a.toString())).join();
  			var result = originalMethod.apply(this, args);
  			var r = JSON.stringify(result);
  			console.log(`Call: ${key}(${a}) => ${r}`);
  			return result;
  		}
  	}

    // return edited descriptor as opposed to overwriting the descriptor
    return descriptor;
}

class Person { 

  public name: string;
  public surname: string;

  constructor(name : string, surname : string) { 
    this.name = name;
    this.surname = surname;
  }
  
  @logMethod
  public saySomething(@logParameter something : string, somethingElse : string) : string { 
    return this.name + " " + this.surname + " says: " + something + " " + somethingElse; 
  }
}

var p = new Person("remo", "jansen");
p.saySomething("I love playing", "halo");
function logProperty(target: any, key: string) {
 
  // property value
  var _val = this[key];
 
  // property getter
  var getter = function () {
    console.log(`Get: ${key} => ${_val}`);
    return _val;
  };
 
  // property setter
  var setter = function (newVal) {
    console.log(`Set: ${key} => ${newVal}`);
    _val = newVal;
  };
 
  // Delete property.
  if (delete this[key]) {
 
    // Create new property with getter and setter
    Object.defineProperty(target, key, {
      get: getter,
      set: setter,
      enumerable: true,
      configurable: true
    });
  }
}

class Person { 
  @logProperty
  public name: string;
  public surname: string;

  constructor(name : string, surname : string) { 
    this.name = name;
    this.surname = surname;
  }
}

var p = new Person("remo", "Jansen");
p.name = "Remo";
var n = p.name;
function logParamTypes(target : any, key : string) {
  var types = Reflect.getMetadata("design:paramtypes", target, key);
  var s = types.map(a => a.name).join();
  console.log(`${key} param types: ${s}`);
}

class Foo {}
interface IFoo {}

class Demo{
  @logParameters
  doSomething(
    param1 : string,
    param2 : number,
    param3 : Foo,
    param4 : { test : string },
    param5 : IFoo,
    param6 : Function,
    param7 : (a : number) => void,
  ) : number {
      return 1
  }
}

// doSomething param types: String, Number, Foo, Object, Object, Function, Function