[Typescript] Decorator - PropertyDecorator, ClassDecorator, MethodDecorator
MethodDecorator
- @Log(level): Console log message
- @Pref: Mesure time
function Log(level: LoggingLevel): MethodDecorator {
return (
target: any,
propertyKey: string | symbol,
descriptor: PropertyDescriptor
) => {
console.log("target", target);
console.log("propertyKey", propertyKey);
console.log("descriptor", descriptor);
const originalFn = descriptor.value;
// Cannot use arrow function
descriptor.value = function (...args: any[]) {
if (level <= appMaxLoggingLevel) {
console.log(
`>> Log: ${propertyKey.toString()}, ${JSON.stringify(args)}`
);
}
originalFn.apply(this, args);
};
};
}
function Pref() {
return (
target: any,
propertyKey: string | symbol,
descriptor: PropertyDescriptor
) => {
const originalFn = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`started at ${new Date().getTime()}`);
originalFn.apply(this, args);
console.log(`ended at ${new Date().getTime()}`);
};
};
}
Usage:
enum LoggingLevel {
ERROR,
INFO,
WARN,
DEBUG,
TRACE,
}
const appMaxLoggingLevel = LoggingLevel.WARN;
class DbService {
// saveData function is wrapped in Log
// Pref wrap both
@Pref()
@Log(LoggingLevel.INFO)
saveData(data: any) {
console.log(`saving data in the database...`);
}
}
const db = new DbService();
db.saveData({ hello: "world" }); // >> Log: saveData, [{"hello":"world"}]
/*
target {}
propertyKey saveData
descriptor {
value: [Function: saveData],
writable: true,
enumerable: false,
configurable: true
}
started at 1668938096069
>> Log: saveData, [{"hello":"world"}]
saving data in the database...
ended at 1668938096069
*/
ClassDecorator
- SealClass: Cannot modify class & it's prototype
// @SealClass()
function SealClass(): ClassDecorator {
return (constructor: Function) => {
Object.seal(constructor);
Object.seal(constructor.prototype);
};
}
// @SealClass
// function SealClass(constructor: Function) {
// Object.seal(constructor);
// Object.seal(constructor.prototype);
// }
Usage:
@SealClass()
class DbService {
// saveData function is wrapped in Log
// Pref wrap both
@Pref()
@Log(LoggingLevel.INFO)
saveData(data: any) {
console.log(`saving data in the database...`);
}
}
const db = new DbService();
// TypeError: Cannot define property sayHello, object is not extensible
Object.defineProperty(DbService, "sayHello", {
value: () => {
console.log("Should error");
},
});
PropertyDecorator
- @DatabaseId: auto generate id if not yet defined, otherwise return generated id
function DatabaseId(): PropertyDecorator {
return (classPrototype: any, propertyKey: string | symbol) => {
Object.defineProperty(classPrototype, propertyKey, {
get: function () {
if (!this["_id"]) {
this["_id"] =
Date.now().toString(36) + Math.random().toString(36).slice(2);
}
return this["_id"];
},
});
};
}
Usage:
class Course {
@DatabaseId()
id: string;
title: string;
constructor(title: string) {
this.title = title;
}
print(message: string) {
console.log(`${message}, Course ${this.title}, id ${this.id}`);
}
}
const course1 = new Course("Typescript");
// Course 1 id: lap621lv3gohkrz1ghf
console.log(`Course 1 id: `, course1.id);
// Course 1: Course { title: 'Typescript', _id: 'lap621lv3gohkrz1ghf' }
console.log(`Course 1: `, course1);
const course2 = new Course("Angular");
// Course 2 id: lap621lwisnfp23ubo
console.log(`Course 2 id: `, course2.id);
// Course 2: Course { title: 'Angular', _id: 'lap621lwisnfp23ubo' }
console.log(`Course 2: `, course2);
----Full Code---
enum LoggingLevel {
ERROR,
INFO,
WARN,
DEBUG,
TRACE,
}
function Log(level: LoggingLevel): MethodDecorator {
return (
target: any,
propertyKey: string | symbol,
descriptor: PropertyDescriptor
) => {
console.log("target", target);
console.log("propertyKey", propertyKey);
console.log("descriptor", descriptor);
const originalFn = descriptor.value;
// Cannot use arrow function
descriptor.value = function (...args: any[]) {
if (level <= appMaxLoggingLevel) {
console.log(
`>> Log: ${propertyKey.toString()}, ${JSON.stringify(args)}`
);
}
originalFn.apply(this, args);
};
};
}
function Pref() {
return (
target: any,
propertyKey: string | symbol,
descriptor: PropertyDescriptor
) => {
const originalFn = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`started at ${new Date().getTime()}`);
originalFn.apply(this, args);
console.log(`ended at ${new Date().getTime()}`);
};
};
}
// @SealClass()
function SealClass(): ClassDecorator {
return (constructor: Function) => {
Object.seal(constructor);
Object.seal(constructor.prototype);
};
}
// @SealClass
// function SealClass(constructor: Function) {
// Object.seal(constructor);
// Object.seal(constructor.prototype);
// }
function DatabaseId(): PropertyDecorator {
return (classPrototype: any, propertyKey: string | symbol) => {
Object.defineProperty(classPrototype, propertyKey, {
get: function () {
if (!this["_id"]) {
this["_id"] =
Date.now().toString(36) + Math.random().toString(36).slice(2);
}
return this["_id"];
},
});
};
}
const appMaxLoggingLevel = LoggingLevel.WARN;
@SealClass()
class DbService {
// saveData function is wrapped in Log
// Pref wrap both
@Pref()
@Log(LoggingLevel.INFO)
saveData(data: any) {
console.log(`saving data in the database...`);
}
}
class Course {
@DatabaseId()
id: string;
title: string;
constructor(title: string) {
this.title = title;
}
print(message: string) {
console.log(`${message}, Course ${this.title}, id ${this.id}`);
}
}
const db = new DbService();
db.saveData({ hello: "world" }); // >> Log: saveData, [{"hello":"world"}]
/*
// TypeError: Cannot define property sayHello, object is not extensible
Object.defineProperty(DbService, "sayHello", {
value: () => {
console.log("Should error");
},
});
*/
/////
const course1 = new Course("Typescript");
// Course 1 id: lap621lv3gohkrz1ghf
console.log(`Course 1 id: `, course1.id);
// Course 1: Course { title: 'Typescript', _id: 'lap621lv3gohkrz1ghf' }
console.log(`Course 1: `, course1);
const course2 = new Course("Angular");
// Course 2 id: lap621lwisnfp23ubo
console.log(`Course 2 id: `, course2.id);
// Course 2: Course { title: 'Angular', _id: 'lap621lwisnfp23ubo' }
console.log(`Course 2: `, course2);