Skip to content

Instantly share code, notes, and snippets.

@remojansen
Last active September 14, 2023 14:54
Show Gist options
  • Save remojansen/16c661a7afd68e22ac6e to your computer and use it in GitHub Desktop.
Save remojansen/16c661a7afd68e22ac6e 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
@Kenya-West
Copy link

Useful examples! Can you @remojansen please provide an example of decorator of async class method that executes only on Promise.resolve()? That will be really useful for sending analytics, for example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment