0

I would like to create a TypeScript decorator that can extend the logic of a property's getter/setter. I have tried to copy the original property under a symbol and call that when I redefine the property. The problem is it turns into an infinite loop.

  //Find the latest version of 'attribute' getter setter in the prototype chain
  let obj = _object;
  while(obj && !(Object.getOwnPropertyDescriptor(obj, 'attribute'))){
    obj = Object.getPrototypeOf(obj);
  }

  //Copy original 'attribute' logic under a symbol
  const attributeDesc = Object.getOwnPropertyDescriptor(obj, 'attribute');
  let id=Symbol('__attribute');
  Object.defineProperty(obj, id, attributeDesc);

  //Redefine 'attribute' logic
  Object.defineProperty(_object, 'attribute', {
    get: () => {
      //call original
      const attribute = obj[id]; //It crashes the page (probably infinite loop)

      //extend original logic
      attribute['extend'] = 'property';

      return attribute;
    },
    enumerable: false,
    configurable: true
  });

If you could explain me why it ends up this way that would help me out. I thought the new getter function reference nothing to do with the original. Please suggest me a solution to achive this in JavaScript.

Thank you for your time and answers!

2
  • This code actually doesn't cause any crashes on my computer. Maybe you don't need to add the property back to original object as a symbol, just do something like const oldContainer = {}; Object.defineProperty(oldContainer, 'attribute', attributeDesc) and then do const attribute = oldContainer.attribute Commented Aug 28, 2021 at 18:20
  • @AlexChashin I created a minimal repro based on your comment: jsfiddle.net/g1fw9q4z/2 Commented Aug 28, 2021 at 19:56

1 Answer 1

1

I don't quite see the error. In the repro you provided, it's logical that there is one: the getter for attribute property is calling itself on the line var attributes = obj[id], so there is an infinite loop. However if you edit your code to be like the snippet you provided in the question:

class A {
  get attribute() {
    return { a: 1 }
  }
}
var _object = new A()

let obj = _object
while (obj && !Object.getOwnPropertyDescriptor(obj, 'attribute')) {
  obj = Object.getPrototypeOf(obj)
}

const attributeDesc = Object.getOwnPropertyDescriptor(obj, 'attribute')
let id = Symbol('__attribute')
Object.defineProperty(obj, id, attributeDesc)

Object.defineProperty(obj, 'attribute', {
  get: function () {
    var attributes = obj[id]
    attributes['extend'] = 'property'
    return attributes
  },
  enumerable: false,
  configurable: true,
})
console.log('result:', obj.attribute)

There is no error and it works as expected. You don't really need the symbol though, you could do something like

function extendAttributes(_object) {
  let obj = _object
  while (obj && !Object.hasOwnProperty(obj, 'attributes')) {
    obj = Object.getPrototypeOf(obj)
  }

  if(!obj) return;

  const oldContainer = {}
  const attributesDescriptor = Object.getOwnPropertyDescriptor(obj, 'attributes')
  Object.defineProperty(oldContainer, 'attributes', attributesDescriptor)

  Object.defineProperty(obj, 'attributes', {
    get() {
      const attribute = oldContainer.attributes; 

      //extend original logic
      attribute['extend'] = 'property';

      return attribute;
    }
  })
}

class A {
  get attributes() { return {a: 1} }
}
const obj = new A()
extendAttributes(obj)
console.log(obj.attributes)

Which also works like expected

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.