<!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
}
}