Decorators - How they work

I'm intrigued to know how JavaScript implements decorators (annotations in Java, Attributes in C#).  I went on a quest and found an interesting page online https://www.sitepoint.com/javascript-decorators-what-they-are/.  But I needed more so  set about experimenting with the language, here are my findings

JavaScript

I first developed some code based on the link above.  I've set my editor to support ES6 and stored it in a .jsx file.

decorators.jsx
function doSomething(name) 
{
    console.log('Hello, ' + name);
}
  
function loggingDecorator(wrapped) 
{
    return function() 
	{
    	console.log('Starting');
      	const result = wrapped.apply(this, arguments);
      	console.log('Finished');
      	return result;
    }
}

function loggingDecorator2(wrapped)
{
    return ()=>
	{
    	console.log('Starting');
      	const result = wrapped.apply(this, arguments);
      	console.log('Finished');
      	return result;
    }
}
  
const wrapped = loggingDecorator(doSomething);
const wrapped2 = loggingDecorator2( doSomething );

function readonly(target, name, descriptor) 
{
    descriptor.writable = false;
    console.log(name);
    return descriptor;
}

class Example 
{
    a() {}
	// an attempt assign a decorator, does not work in regulator ES3 and ES5, you will get an error on the b(){} declaration
    @readonly
    b() {}

    fooBar()
    {
        this.a();
        this.b();
    }
}
  
function  fooBar()
{
	doSomething('selvyn');

	wrapped('tomo');
	wrapped2('michelle');
}

In regular JavaScript lines 56 and 57 perform some very clever magic.  Lines 28 and 29 declare two variable that are actually anonymous functions.  Yes that seems crazy because lines 28/29 look like they are getting a reference to a function, but that's not the case, they are receiving the result of loggingDecorator() and loggingDecorator2() respectively (they're just regular function calls - if we had written const wrapped = loggingDecorator; then that would be a function reference). The type returned from both functions is an anonymous function, so it makes sense.

So JavaSciprt being JavaScipt allows you to declare a function with no parameters, then call it with parameters that can be accessed within the function through the arguments variable that is an array.  Hence lines 11 and 22 work, and the calls at 56 and 57 work.

TypeScript

I then develop a version of the code in typescript which clearly allowed me to use the decorator syntax in the file but not in a browser.  The file extension is .ts

decorators.ts
function doSomething(name) 
{
	console.log('Hello, ' + name);
}
  
function loggingDecorator(wrapped) 
{
    return function(args) 
	{
    	console.log('Starting');
      	const result = wrapped.apply(this, [args]);
      	console.log('Finished');
      	return result;
    }
}

function loggingDecorator2(wrapped)
{
    return (args)=>
	{
      	console.log('Starting');
      	const result = wrapped.apply(this, [args]);
      	console.log('Finished');
      	return result;
    }
}
  
let loggingDecorator3 = (name, wrapped)=>
{
    return ()=>
	{
      console.log('Starting');
      const result = wrapped(name);
      console.log('Finished');
      return result;
    }
}
  
const wrapped = (name)=>{const fn=loggingDecorator(doSomething); fn(name);};
const wrapped2 = (name)=>{const fn=loggingDecorator2(doSomething);fn(name);};
const wrapped4 = (name)=>{const fn=loggingDecorator3(name, doSomething); fn();};

function readonly(target, name, descriptor) 
{
    descriptor.writable = false;
    console.log(name);
    return descriptor;
}

class Example 
{
    a() {}
    @readonly
    b() {}

    fooBar()
    {
        this.a();
        this.b();
    }
}
  
function  fooBar()
{
	doSomething('selvyn');

	wrapped('tomo');
    wrapped2('michelle');

    let x = loggingDecorator3('Alison', doSomething);
    x();

    wrapped4('tomo');

    const e = new Example();
}

Now look the three loggingDecorator functions we have defined.  Because TypeScript is strict, the code in the .js file won't compile.  So we have rewrite the our wrapped declarations, see lines 39-41.  We use the arrow function to call the loggingDecorator() functions.  Once the wrapped functions are declared we can use them as shown in lines 67-73.

Decorator Pattern Implementation

 So as can be from the code samples above in both pure JavaScript and TypeScript, the loggingDecorator() decorates the doSomething() function.