<!DOCTYPE html>
<html>
  <body>
    <script src="script.js"></script>
    <script>
      var my = new MyClass();
      my.mMyField = 43;
      my.mMyField = 44;
    </script>
   </body>
</html>
class MyClass {
	@OnChange(MyClass.onFieldChange)
	public mMyField: number = 42;

	static onFieldChange(self: MyClass, newVal: number): void {
    document.body.innerHTML +=
      "<li>Changing from " + self.mMyField + " to " + newVal + "</li>";
	}
}

function OnChange<ClassT, T>(callback: (ClassT, T) => void): any {
    return (target: Object, propertyKey: string | symbol) => {
      // Необходимо задействовать существующий дескриптор, если он есть.
      // Это позволит объявять несколько декораторов на одном свойстве.
      var descriptor = Object.getOwnPropertyDescriptor(target, propertyKey)
        || {configurable: true, enumerable: true};
      // Подменяем или объявляем get и set
      var value: T;
      var originalGet = descriptor.get || (() => value);
      var originalSet = descriptor.set || (val => value = val);
      descriptor.get = originalGet;
      descriptor.set = function(newVal: T) {
        // Внимание, если определяем set через function, 
        // то this - текущий экземпляр класса,
        // если через лямбду, то this - Window!!!
        var currentVal = originalGet.call(this);
        if (newVal != currentVal) {
        	// Вызываем статический метод callback с двумя параметрами
          callback.call(target.constructor, this, newVal);
        }
        originalSet.call(this, newVal);
      };
      // Объявляем новое свойство, либо обновляем дескриптор
      Object.defineProperty(target, propertyKey, descriptor);
      return descriptor;
    }
}
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": true,
    "suppressImplicitAnyIndexErrors": true
  }
}