JavaScript use original getter/setter in defineProperty

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!

Answer

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