Life Inside an IDE

Refactoring with the Help of Proxies

Thoughts from 25th April 2019

Today I started refactoring part of an application from one API version to another. In the two versions, some fields are named differently, for example title was renamed to jobTitle. This would be an awesome use-case for TypeScript, but this was legacy code and I needed to get this done as fast as possible.

Even so, I still wanted a way to get notified when the old properties were used anywhere in the application. Luckily, you can do this with proxies!

function wrapInRefactorProxy (wrappedObject, whitelistedKeys) {
  return new Proxy(wrappedObject, {
    get: (object, key) => {
      if (!whitelistedKeys.includes(key)) {
        console.error('Context for the following error', object)
        throw new Error(`Attempted to access non-whitelisted property "${key}"`)
      }

      return object[key]
    }
  })
}

This will throw a runtime error whenever a non-whitelisted property is accessed. You can use the wrapper like this:

const person = wrapInRefactorProxy(
  { id: '123123', name: 'Something' },
  [ 'id', 'name', 'jobTitle' ]
)

console.log(person.id) // -> 123123
console.log(person.jobTitle) // -> undefined
console.log(person.title) // -> throw Error

Be aware that if you are using Object.assign, the proxy will get lost:

const assignedPerson = Object.assign({}, person)

console.log(assignedPerson.id) // -> 123123
console.log(assignedPerson.jobTitle) // -> undefined
console.log(assignedPerson.title) // -> undefined (!)

It's not even close to the comfort of TypeScript, but for me it was better than nothing. I could change the API, fix the runtime errors one by one, and at the end had something working without having to do too much digging.