Wallace
Wallace

Reputation: 17429

Use a TypeScript property descriptor to replace the accessors, while preserving composability

How does one implement a TypeScript property descriptor that replaces the accessors, while maintaining composability?

Upvotes: 2

Views: 1850

Answers (1)

Wallace
Wallace

Reputation: 17429

The TypeScript handbook Decorators chapter seems to imply that it cannot be done, or at least that it is a bad idea. It says...

...there is currently no mechanism to describe an instance property when defining members of a prototype, and no way to observe or modify the initializer for a property. As such, a property decorator can only be used to observe that a property of a specific name has been declared for a class.

However, I found the Object.getOwnPropertyDescriptor(target, key) which seems to provide what is needed.

Here is an example:

function decorator(name: string) {
    return function (target: any, key: string) {
        try {
            console.log(`${name}...`);
            let localValue = undefined;
            let prev = Object.getOwnPropertyDescriptor(target, key);
            let setter = function (newVal) {
                try {
                    console.log(`${name} setter(${newVal}) called...`);
                    if (prev) prev.set(newVal);
                    localValue = newVal;
                } finally {
                    console.log(`...${name} setter called`);
                }
            };
            let getter = function () {
                try {
                    console.log(`${name} getter(${localValue}) called...`);
                    if (prev) prev.get();
                    return localValue;
                } finally {
                    console.log(`...${name} getter called`);
                }
            };
            Object.defineProperty(target, key, {
                get: getter,
                set: setter,
                enumerable: prev == null ? true : prev.enumerable,
                configurable: prev == null ? true : prev.configurable
            });
        } finally {
            console.log(`...${name}`);
        }
    }
}

class MyClass {
    @decorator("decorator1")
    @decorator("decorator2")
    myProperty: string;
}

var mc = new MyClass();
mc.myProperty = "asdf";
console.log(mc.myProperty);

The output is:

decorator2...
...decorator2
decorator1...
...decorator1
decorator1 setter(asdf) called...
decorator2 setter(asdf) called...
...decorator2 setter called
...decorator1 setter called
decorator1 getter(asdf) called...
decorator2 getter(asdf) called...
...decorator2 getter called
...decorator1 getter called
asdf

I am not at all sure this is the right way to do this. I would love to receive comments.

Upvotes: 2

Related Questions