Skip to content

Instantly share code, notes, and snippets.

@martinobordin
Forked from remojansen/class_decorator.ts
Created May 8, 2018 15:38
Show Gist options
  • Save martinobordin/40379be32764cf107ed89d2b29576e57 to your computer and use it in GitHub Desktop.
Save martinobordin/40379be32764cf107ed89d2b29576e57 to your computer and use it in GitHub Desktop.
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment