├── .gitignore
├── README.md
├── img
├── decorator-evalution-execution.png
├── decorator-sample.png
└── signatures.png
├── index.html
├── package.json
├── src
├── class-decorator.ts
├── decorators.ts
├── gist
│ ├── gist-accessor-decorator.js
│ ├── gist-accessor-decorator.ts
│ ├── gist-class-decorator.js
│ ├── gist-class-decorator.ts
│ ├── gist-decorator.ts
│ ├── gist-method-decorator.js
│ ├── gist-method-decorator.ts
│ ├── gist-parameter-decorator.js
│ ├── gist-parameter-decorator.ts
│ ├── gist-property-decorator.js
│ ├── gist-property-decorator.ts
│ ├── metadata-reflection-api.js
│ └── metadata-reflection-api.ts
├── index.ts
├── method-decorator.ts
├── parameter-decorator.ts
└── property-decorator.ts
├── tsconfig.json
├── webpack.config.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | node_modules
3 |
4 | # production
5 | dist
6 |
7 | # misc
8 | npm-debug.log
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TypeScript decorator #
2 | This project helps to understand decorators and different kinds of decorators and its samples.
3 |
4 | To run this project
5 | 1. Clone this git repository
6 | 2. run npm start
7 | 3. host the root directory via ```http-server``` [if you dont have http-server. Run this command ```npm i http-server --g```]
--------------------------------------------------------------------------------
/img/decorator-evalution-execution.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohanramphp/typescript-decorators/0a13b2a15e311fb6fef5fa9d3060329c14e5562f/img/decorator-evalution-execution.png
--------------------------------------------------------------------------------
/img/decorator-sample.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohanramphp/typescript-decorators/0a13b2a15e311fb6fef5fa9d3060329c14e5562f/img/decorator-sample.png
--------------------------------------------------------------------------------
/img/signatures.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohanramphp/typescript-decorators/0a13b2a15e311fb6fef5fa9d3060329c14e5562f/img/signatures.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TypeScript Decorators
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "typescript-decorator",
3 | "version": "0.0.1",
4 | "description": "",
5 | "scripts": {
6 | "prestart": "yarn",
7 | "start": "webpack --watch",
8 | "dev-server": "webpack-dev-server --hotOnly"
9 | },
10 | "keywords": [],
11 | "author": "Mohan Ram",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "@webpack-cli/serve": "^0.1.2",
15 | "ts-loader": "^5.3.1",
16 | "typescript": "^3.2.2",
17 | "webpack": "^4.20.2",
18 | "webpack-cli": "^3.1.2",
19 | "webpack-dev-server": "^3.1.10"
20 | },
21 | "dependencies": {
22 | "reflect-metadata": "^0.1.12"
23 | }
24 | }
--------------------------------------------------------------------------------
/src/class-decorator.ts:
--------------------------------------------------------------------------------
1 | export function logClass(target: Function) {
2 | // save a reference to the original constructor
3 | const original = target;
4 |
5 | // a utility function to generate instances of a class
6 | function construct(constructor, args) {
7 | const c: any = function () {
8 | return constructor.apply(this, args);
9 | }
10 | c.prototype = constructor.prototype;
11 | return new c();
12 | }
13 |
14 | // the new constructor behaviour
15 | const f: any = function (...args) {
16 | console.log("New: " + original['name']);
17 | return construct(original, args);
18 | }
19 |
20 | // copy prototype so intanceof operator still works
21 | f.prototype = original.prototype;
22 |
23 | // return new constructor (will override original)
24 | return f;
25 | }
--------------------------------------------------------------------------------
/src/decorators.ts:
--------------------------------------------------------------------------------
1 | import { logClass } from './class-decorator';
2 | import { logMethod } from './method-decorator';
3 | import { logProperty } from './property-decorator';
4 | import { logParameter } from './parameter-decorator';
5 |
6 | export function log(...args) {
7 | switch (args.length) {
8 | case 3:
9 | if (typeof args[2] === "number") {
10 | return logParameter.apply(this, args);
11 | }
12 | return logMethod.apply(this, args);
13 | case 2:
14 | return logProperty.apply(this, args);
15 | case 1:
16 | return logClass.apply(this, args);
17 | default:
18 | throw new Error('Not a valid decorator');
19 | }
20 | }
--------------------------------------------------------------------------------
/src/gist/gist-accessor-decorator.js:
--------------------------------------------------------------------------------
1 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5 | return c > 3 && r && Object.defineProperty(target, key, r), r;
6 | };
7 | function enumerable(value) {
8 | return function (target, propertyKey, descriptor) {
9 | console.log('decorator - sets the enumeration part of the accessor');
10 | descriptor.enumerable = value;
11 | };
12 | }
13 | var Employee = /** @class */ (function () {
14 | function Employee() {
15 | }
16 | Object.defineProperty(Employee.prototype, "salary", {
17 | get: function () { return "Rs. " + this._salary; },
18 | set: function (salary) { this._salary = +salary; },
19 | enumerable: true,
20 | configurable: true
21 | });
22 | Object.defineProperty(Employee.prototype, "name", {
23 | get: function () {
24 | return "Sir/Madam, " + this._name;
25 | },
26 | set: function (name) {
27 | this._name = name;
28 | },
29 | enumerable: true,
30 | configurable: true
31 | });
32 | __decorate([
33 | enumerable(false)
34 | ], Employee.prototype, "salary", null);
35 | __decorate([
36 | enumerable(true)
37 | ], Employee.prototype, "name", null);
38 | return Employee;
39 | }());
40 | var emp = new Employee();
41 | emp.salary = 1000;
42 | for (var prop in emp) {
43 | console.log("enumerable property = " + prop);
44 | }
45 | // salary property - will not be part of enumeration since we configured it to false
46 | // output:
47 | // enumerable property = _salary
48 |
--------------------------------------------------------------------------------
/src/gist/gist-accessor-decorator.ts:
--------------------------------------------------------------------------------
1 |
2 | function enumerable(value: boolean) {
3 | return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
4 | console.log('decorator - sets the enumeration part of the accessor');
5 | descriptor.enumerable = value;
6 | };
7 | }
8 |
9 | class Employee {
10 | private _salary: number;
11 | private _name: string;
12 |
13 | @enumerable(false)
14 | get salary() { return `Rs. ${this._salary}`; }
15 |
16 | set salary(salary: any) { this._salary = +salary; }
17 |
18 | @enumerable(true)
19 | get name() {
20 | return `Sir/Madam, ${this._name}`;
21 | }
22 |
23 | set name(name: string) {
24 | this._name = name;
25 | }
26 |
27 | }
28 |
29 | const emp = new Employee();
30 | emp.salary = 1000;
31 | for (let prop in emp) {
32 | console.log(`enumerable property = ${prop}`);
33 | }
34 | // salary property - will not be part of enumeration since we configured it to false
35 | // output:
36 | // decorator - sets the enumeration part of the accessor
37 | // decorator - sets the enumeration part of the accessor
38 | // enumerable property = _salary
39 | // enumerable property = name
40 |
--------------------------------------------------------------------------------
/src/gist/gist-class-decorator.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6 | return c > 3 && r && Object.defineProperty(target, key, r), r;
7 | };
8 | Object.defineProperty(exports, "__esModule", { value: true });
9 | function logClass(target) {
10 | // save a reference to the original constructor
11 | var original = target;
12 | // a utility function to generate instances of a class
13 | function construct(constructor, args) {
14 | var c = function () {
15 | return constructor.apply(this, args);
16 | };
17 | c.prototype = constructor.prototype;
18 | return new c();
19 | }
20 | // the new constructor behaviour
21 | var f = function () {
22 | var args = [];
23 | for (var _i = 0; _i < arguments.length; _i++) {
24 | args[_i] = arguments[_i];
25 | }
26 | console.log("New: " + original['name'] + " is created");
27 | return construct(original, args);
28 | };
29 | // copy prototype so intanceof operator still works
30 | f.prototype = original.prototype;
31 | // return new constructor (will override original)
32 | return f;
33 | }
34 | exports.logClass = logClass;
35 | var Employee = /** @class */ (function () {
36 | function Employee() {
37 | }
38 | Employee = __decorate([
39 | logClass
40 | ], Employee);
41 | return Employee;
42 | }());
43 | var emp = new Employee();
44 | console.log('emp instanceof Employee');
45 | console.log(emp instanceof Employee);
46 |
--------------------------------------------------------------------------------
/src/gist/gist-class-decorator.ts:
--------------------------------------------------------------------------------
1 | export function logClass(target: Function) {
2 | // save a reference to the original constructor
3 | const original = target;
4 |
5 | // a utility function to generate instances of a class
6 | function construct(constructor, args) {
7 | const c: any = function () {
8 | return constructor.apply(this, args);
9 | }
10 | c.prototype = constructor.prototype;
11 | return new c();
12 | }
13 |
14 | // the new constructor behaviour
15 | const f: any = function (...args) {
16 | console.log(`New: ${original['name']} is created`);
17 | return construct(original, args);
18 | }
19 |
20 | // copy prototype so intanceof operator still works
21 | f.prototype = original.prototype;
22 |
23 | // return new constructor (will override original)
24 | return f;
25 | }
26 |
27 | @logClass
28 | class Employee {
29 |
30 | }
31 |
32 | let emp = new Employee();
33 | console.log('emp instanceof Employee');
34 | console.log(emp instanceof Employee);
--------------------------------------------------------------------------------
/src/gist/gist-decorator.ts:
--------------------------------------------------------------------------------
1 | import { logClass } from './class-decorator';
2 | import { logMethod } from './method-decorator';
3 | import { logProperty } from './property-decorator';
4 | import { logParameter } from './parameter-decorator';
5 |
6 | export function log(...args) {
7 | switch (args.length) {
8 | case 3:
9 | if (typeof args[2] === "number") {
10 | return logParameter.apply(this, args);
11 | }
12 | return logMethod.apply(this, args);
13 | case 2:
14 | return logProperty.apply(this, args);
15 | case 1:
16 | return logClass.apply(this, args);
17 | default:
18 | throw new Error('Not a valid decorator');
19 | }
20 | }
21 |
22 | @log
23 | class Employee {
24 |
25 | @log
26 | private name: string;
27 |
28 | constructor(name: string) {
29 | this.name = name;
30 | }
31 |
32 | @log
33 | greet(@log message: string): string {
34 | return `${this.name} says: ${message}`;
35 | }
36 |
37 | }
--------------------------------------------------------------------------------
/src/gist/gist-method-decorator.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __decorate = (this && this.__decorate) ||
3 | function (decorators, target, key, desc) {
4 | // function argument length
5 | var c = arguments.length
6 |
7 | /**
8 | * manipulating the result
9 | * if - only the array of decorators and target is passed
10 | * then - it should be an class decorator
11 | * else - if the descriptor (4th argument) is null
12 | * then - preparing the property descriptor with known values
13 | * otherwise - use same descriptor
14 | */
15 |
16 | var r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc;
17 |
18 | // declaring this variable to hold the decorator
19 | var d;
20 |
21 | // if - native reflection part is available use that to trigger the decorators
22 | // else - fallback
23 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
24 | r = Reflect.decorate(decorators, target, key, desc)
25 | }
26 | else {
27 | // iterating through the decorators from right to left
28 | for (var i = decorators.length - 1; i >= 0; i--) {
29 | //if - decorator is valid assign to d
30 | if (d = decorators[i]) {
31 | /**
32 | * if - only the array of decorators and target is passed
33 | * then - it should be an class decorator - call the decorator by passing target
34 | * else - if all 4 arguments are there then its method decorator and call correspondingly
35 | * if argument passed is 3, then probably its property descriptor and call correspondingly
36 | * no condition satisfied - return the manipulated result
37 | */
38 | r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r
39 | }
40 | }
41 | };
42 |
43 | /**
44 | * since only the method decorator need to redefine its property with applied decorator result.
45 | * finally returning manipulated r
46 | */
47 | return c > 3 && r && Object.defineProperty(target, key, r), r;
48 | };
49 |
50 | exports.__esModule = true;
51 | function logMethod(target, propertyName, propertyDesciptor) {
52 | // target === Employee.prototype
53 | // propertyName === "greet"
54 | // propertyDesciptor === Object.getOwnPropertyDescriptor(Employee.prototype, "greet")
55 | var method = propertyDesciptor.value;
56 | propertyDesciptor.value = function () {
57 | var args = [];
58 | for (var _i = 0; _i < arguments.length; _i++) {
59 | args[_i] = arguments[_i];
60 | }
61 | // convert list of greet arguments to string
62 | var params = args.map(function (a) { return JSON.stringify(a); }).join();
63 | // invoke greet() and get its return value
64 | var result = method.apply(this, args);
65 | // convert result to string
66 | var r = JSON.stringify(result);
67 | // display in console the function call details
68 | console.log("Call: " + propertyName + "(" + params + ") => " + r);
69 | // return the result of invoking the method
70 | return result;
71 | };
72 | return propertyDesciptor;
73 | }
74 | exports.logMethod = logMethod;
75 | ;
76 | var Employee = /** @class */ (function () {
77 | function Employee(firstName, lastName) {
78 | this.firstName = firstName;
79 | this.lastName = lastName;
80 | }
81 | Employee.prototype.greet = function (message) {
82 | return this.firstName + " " + this.lastName + " says: " + message;
83 | };
84 |
85 | // typescript calls helper `__decorate`
86 | // to apply the decorator on the object's prototype
87 | __decorate([
88 | logMethod
89 | ], Employee.prototype, "greet");
90 | return Employee;
91 | }());
92 | var emp = new Employee('Mohan Ram', 'Ratnakumar');
93 | emp.greet('hello');
94 |
--------------------------------------------------------------------------------
/src/gist/gist-method-decorator.ts:
--------------------------------------------------------------------------------
1 |
2 | export function logMethod(
3 | target: Object,
4 | propertyName: string,
5 | propertyDesciptor: PropertyDescriptor): PropertyDescriptor {
6 | // target === Employee.prototype
7 | // propertyName === "greet"
8 | // propertyDesciptor === Object.getOwnPropertyDescriptor(Employee.prototype, "greet")
9 | const method = propertyDesciptor.value;
10 |
11 | propertyDesciptor.value = function (...args: any[]) {
12 |
13 | // convert list of greet arguments to string
14 | const params = args.map(a => JSON.stringify(a)).join();
15 |
16 | // invoke greet() and get its return value
17 | const result = method.apply(this, args);
18 |
19 | // convert result to string
20 | const r = JSON.stringify(result);
21 |
22 | // display in console the function call details
23 | console.log(`Call: ${propertyName}(${params}) => ${r}`);
24 |
25 | // return the result of invoking the method
26 | return result;
27 | }
28 | return propertyDesciptor;
29 | };
30 |
31 | class Employee {
32 |
33 | constructor(
34 | private firstName: string,
35 | private lastName: string
36 | ) {
37 | }
38 |
39 | @logMethod
40 | greet(message: string): string {
41 | return `${this.firstName} ${this.lastName} says: ${message}`;
42 | }
43 |
44 | }
45 |
46 | const emp = new Employee('Mohan Ram', 'Ratnakumar');
47 | emp.greet('hello');
--------------------------------------------------------------------------------
/src/gist/gist-parameter-decorator.js:
--------------------------------------------------------------------------------
1 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5 | return c > 3 && r && Object.defineProperty(target, key, r), r;
6 | };
7 | var __param = (this && this.__param) || function (paramIndex, decorator) {
8 | return function (target, key) { decorator(target, key, paramIndex); }
9 | };
10 | function logParameter(target, propertyName, index) {
11 | // generate metadatakey for the respective method
12 | // to hold the position of the decorated parameters
13 | var metadataKey = "log_" + propertyName + "_parameters";
14 | if (Array.isArray(target[metadataKey])) {
15 | target[metadataKey].push(index);
16 | }
17 | else {
18 | target[metadataKey] = [index];
19 | }
20 | }
21 | var Employee = /** @class */ (function () {
22 | function Employee() {
23 | }
24 | Employee.prototype.greet = function (message) {
25 | return "hello " + message;
26 | };
27 | __decorate([
28 | __param(0, logParameter)
29 | ], Employee.prototype, "greet");
30 | return Employee;
31 | }());
32 | var emp = new Employee();
33 | emp.greet('hello');
34 |
--------------------------------------------------------------------------------
/src/gist/gist-parameter-decorator.ts:
--------------------------------------------------------------------------------
1 | function logParameter(target: Object, propertyName: string, index: number) {
2 |
3 | // generate metadatakey for the respective method
4 | // to hold the position of the decorated parameters
5 | const metadataKey = `log_${propertyName}_parameters`;
6 | if (Array.isArray(target[metadataKey])) {
7 | target[metadataKey].push(index);
8 | }
9 | else {
10 | target[metadataKey] = [index];
11 | }
12 | }
13 |
14 | class Employee {
15 | greet(@logParameter message: string): string {
16 | return `hello ${message}`;
17 | }
18 | }
19 | const emp = new Employee();
20 | emp.greet('hello');
21 |
--------------------------------------------------------------------------------
/src/gist/gist-property-decorator.js:
--------------------------------------------------------------------------------
1 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5 | return c > 3 && r && Object.defineProperty(target, key, r), r;
6 | };
7 | function logParameter(target, propertyName) {
8 | // property value
9 | var _val = this[propertyName];
10 | // property getter method
11 | var getter = function () {
12 | console.log("Get: " + propertyName + " => " + _val);
13 | return _val;
14 | };
15 | // property setter method
16 | var setter = function (newVal) {
17 | console.log("Set: " + propertyName + " => " + newVal);
18 | _val = newVal;
19 | };
20 | // Delete property.
21 | if (delete this[propertyName]) {
22 | // Create new property with getter and setter
23 | Object.defineProperty(target, propertyName, {
24 | get: getter,
25 | set: setter,
26 | enumerable: true,
27 | configurable: true
28 | });
29 | }
30 | }
31 | var Employee = /** @class */ (function () {
32 | function Employee() {
33 | }
34 | __decorate([
35 | logParameter
36 | ], Employee.prototype, "name");
37 | return Employee;
38 | }());
39 | var emp = new Employee();
40 | emp.name = 'Mohan Ram'; // Set: name => Mohan Ram
41 | console.log(emp.name); // Get: name => Mohan Ram
42 |
--------------------------------------------------------------------------------
/src/gist/gist-property-decorator.ts:
--------------------------------------------------------------------------------
1 | function logParameter(target: Object, propertyName: string) {
2 |
3 | // property value
4 | let _val = this[propertyName];
5 |
6 | // property getter method
7 | const getter = () => {
8 | console.log(`Get: ${propertyName} => ${_val}`);
9 | return _val;
10 | };
11 |
12 | // property setter method
13 | const setter = newVal => {
14 | console.log(`Set: ${propertyName} => ${newVal}`);
15 | _val = newVal;
16 | };
17 |
18 | // Delete property.
19 | if (delete this[propertyName]) {
20 |
21 | // Create new property with getter and setter
22 | Object.defineProperty(target, propertyName, {
23 | get: getter,
24 | set: setter,
25 | enumerable: true,
26 | configurable: true
27 | });
28 | }
29 | }
30 |
31 | class Employee {
32 |
33 | @logParameter
34 | name: string;
35 |
36 | }
37 |
38 | const emp = new Employee();
39 | emp.name = 'Mohan Ram';
40 | console.log(emp.name);
41 | // Set: name => Mohan Ram
42 | // Get: name => Mohan Ram
43 | // Mohan Ram
--------------------------------------------------------------------------------
/src/gist/metadata-reflection-api.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6 | return c > 3 && r && Object.defineProperty(target, key, r), r;
7 | };
8 | var __param = (this && this.__param) || function (paramIndex, decorator) {
9 | return function (target, key) { decorator(target, key, paramIndex); }
10 | };
11 | exports.__esModule = true;
12 | require("reflect-metadata");
13 | // parameter decorator uses reflect api to store the indexes of the decorated parameter
14 | function logParameter(target, propertyName, index) {
15 | var indices = Reflect.getMetadata("log_" + propertyName + "_parameters", target, propertyName) || [];
16 | indices.push(index);
17 | Reflect.defineMetadata("log_" + propertyName + "_parameters", indices, target, propertyName);
18 | }
19 | exports.logParameter = logParameter;
20 | // property decorator uses reflect api to get the run time type of the property
21 | function logProperty(target, propertyName) {
22 | var t = Reflect.getMetadata("design:type", target, propertyName);
23 | console.log(propertyName + " type: " + t.name);
24 | }
25 | exports.logProperty = logProperty;
26 | var Employee = /** @class */ (function () {
27 | function Employee(name) {
28 | this.name = name;
29 | }
30 | Employee.prototype.greet = function (message) {
31 | return this.name + " says: " + message;
32 | };
33 | __decorate([
34 | logProperty
35 | ], Employee.prototype, "name");
36 | __decorate([
37 | __param(0, logParameter)
38 | ], Employee.prototype, "greet");
39 | return Employee;
40 | }());
41 |
--------------------------------------------------------------------------------
/src/gist/metadata-reflection-api.ts:
--------------------------------------------------------------------------------
1 | import "reflect-metadata";
2 |
3 | // parameter decorator uses reflect api to store the indexes of the decorated parameter
4 | export function logParameter(target: Object, propertyName: string, index: number) {
5 | const indices = Reflect.getMetadata(`log_${propertyName}_parameters`, target, propertyName) || [];
6 | indices.push(index);
7 | Reflect.defineMetadata(`log_${propertyName}_parameters`, indices, target, propertyName);
8 | }
9 |
10 | // property decorator uses reflect api to get the run time type of the property
11 | export function logProperty(target: Object, propertyName: string): void {
12 | var t = Reflect.getMetadata("design:type", target, propertyName);
13 | console.log(`${propertyName} type: ${t.name}`);
14 | }
15 |
16 |
17 | class Employee {
18 |
19 | @logProperty
20 | private name: string;
21 |
22 | constructor(name: string) {
23 | this.name = name;
24 | }
25 |
26 | greet(@logParameter message: string): string {
27 | return `${this.name} says: ${message}`;
28 | }
29 |
30 | }
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { logMethod, configureMethod } from './method-decorator';
2 | import { logProperty } from './property-decorator';
3 | import { logParameter } from './parameter-decorator';
4 | import { logClass } from './class-decorator';
5 | import { log } from './decorators';
6 |
7 |
8 | class JoiningKit {
9 |
10 | }
11 |
12 | @logClass
13 | class Employee {
14 |
15 | @logProperty
16 | private firstName: string;
17 | private lastName: string;
18 |
19 | @logProperty
20 | private joiningKit: JoiningKit;
21 |
22 | @logProperty
23 | private age: number;
24 |
25 | constructor(firstName: string, lastName: string) {
26 | this.firstName = firstName;
27 | this.lastName = lastName;
28 | }
29 |
30 | @logMethod
31 | @configureMethod({ enumerable: false })
32 | greet(@logParameter message: string): string {
33 | return `${this.firstName} ${this.lastName} says: ${message}`;
34 | }
35 |
36 | getJoiningKit() {
37 | return this.joiningKit;
38 | }
39 |
40 | getAge() {
41 | return this.age;
42 | }
43 |
44 | setJoiningKit(joiningKit: JoiningKit) {
45 | return this.joiningKit = joiningKit;
46 | }
47 | }
48 |
49 | const emp = new Employee('Mohan Ram', 'Ratnakumar');
50 | emp.greet('hello');
51 | emp.getJoiningKit();
52 | emp.getAge();
53 | for (let i in emp) {
54 | console.log(i);
55 | }
56 |
57 | @log
58 | class Person {
59 |
60 | constructor(
61 | private firstName: string,
62 | private lastName: string
63 | ) {
64 | }
65 |
66 | @log
67 | getFullName() {
68 | return `${this.firstName} ${this.lastName}`;
69 | }
70 | }
71 |
72 | const person = new Person('Mohan', 'Ram');
73 | person.getFullName();
--------------------------------------------------------------------------------
/src/method-decorator.ts:
--------------------------------------------------------------------------------
1 | import "reflect-metadata";
2 |
3 | export function configureMethod(
4 | { enumerable = true, configurable = true, writable = true }: { [key: string]: boolean }
5 | ) {
6 | return function (
7 | target: Object,
8 | propertyName: string,
9 | propertyDesciptor: PropertyDescriptor
10 | ): PropertyDescriptor {
11 | propertyDesciptor.enumerable = enumerable;
12 | propertyDesciptor.configurable = configurable;
13 | propertyDesciptor.writable = writable;
14 | console.log(propertyDesciptor);
15 | return propertyDesciptor;
16 | }
17 | }
18 | export function logMethod(
19 | target: Object,
20 | propertyName: string,
21 | propertyDesciptor: PropertyDescriptor): PropertyDescriptor {
22 | // target === Employee.prototype
23 | // propertyName === "greet"
24 | // propertyDesciptor === Object.getOwnPropertyDescriptor(Employee.prototype, "greet")
25 | const method = propertyDesciptor.value;
26 |
27 | propertyDesciptor.value = function (...args: any[]) {
28 |
29 | //const indices = Reflect.getMetadata(`log_${propertyName}_parameters`, target, propertyName);
30 | const indices = target[`log_${propertyName}_parameters`];
31 | if (Array.isArray(indices)) {
32 | args.forEach((arg, index) => {
33 | if (indices.indexOf(index) !== -1) {
34 | var argStr = JSON.stringify(arg) || arg.toString();
35 | console.log(`logged parameters arg[${index}]: ${argStr}`);
36 | }
37 | });
38 | }
39 | // convert list of foo arguments to string
40 | const params = args.map(a => JSON.stringify(a)).join();
41 |
42 | // invoke foo() and get its return value
43 | const result = method.apply(this, args);
44 |
45 | // convert result to string
46 | const r = JSON.stringify(result);
47 |
48 | // display in console the function call details
49 | console.log(`Call: ${propertyName}(${params}) => ${r}`);
50 |
51 | // return the result of invoking the method
52 | return result;
53 | }
54 | return propertyDesciptor;
55 | };
56 |
--------------------------------------------------------------------------------
/src/parameter-decorator.ts:
--------------------------------------------------------------------------------
1 | import "reflect-metadata";
2 |
3 | export function logParameter(target: Object, propertyName: string, index: number) {
4 | const metadataKey = `log_${propertyName}_parameters`;
5 | if (Array.isArray(target[metadataKey])) {
6 | target[metadataKey].push(index);
7 | }
8 | else {
9 | target[metadataKey] = [index];
10 | }
11 | /* const indices = Reflect.getMetadata(`log_${propertyName}_parameters`, target, propertyName) || [];
12 | indices.push(index);
13 | Reflect.defineMetadata(`log_${propertyName}_parameters`, indices, target, propertyName); */
14 | }
--------------------------------------------------------------------------------
/src/property-decorator.ts:
--------------------------------------------------------------------------------
1 | import "reflect-metadata";
2 |
3 | export function logProperty(target: Object, propertyName: string): void {
4 | var t = Reflect.getMetadata("design:type", target, propertyName);
5 | console.log(`${propertyName} type: ${t.name}`);
6 | }
7 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": [
3 | "src/*.ts",
4 | "src/**/*.ts"
5 | ],
6 | "exclude": [
7 | "./dist",
8 | "./node_modules",
9 | "./src/gist"
10 | ],
11 | "compilerOptions": {
12 | "rootDir": "./src",
13 | "outDir": "./dist/",
14 | "noImplicitAny": false,
15 | "sourceMap": true,
16 | "module": "commonjs",
17 | "target": "es5",
18 | "experimentalDecorators": true,
19 | "emitDecoratorMetadata": true,
20 | }
21 | }
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 |
4 | module.exports = {
5 | entry: './src/index.ts',
6 | output: {
7 | filename: 'bundle.js',
8 | path: path.resolve(__dirname, 'dist')
9 | },
10 | devtool: 'inline-source-map',
11 | devServer: {
12 | contentBase: './',
13 | watchContentBase: true
14 | },
15 | plugins: [
16 | new webpack.HotModuleReplacementPlugin()
17 | ],
18 | module: {
19 | rules: [
20 | {
21 | test: /\.ts?$/,
22 | use: 'ts-loader',
23 | exclude: [/node_modules/, /gist/]
24 | }
25 | ]
26 | },
27 | resolve: {
28 | extensions: ['.ts', '.js']
29 | },
30 | mode: 'development'
31 | };
--------------------------------------------------------------------------------