3

I would like to extend the String prototype and use it in one or more components of my Angular 5 application. The main reason is that I have a series of objects, which have properties that sometimes are populated, and sometimes not. Example: customer.address is sometimes a string (the _id of the address) and sometimes it's populated and it's an Address object, with an _id property.

What I want is basically not have to worry whether customer.address is populated or not, and simply access the _id. I know it's considered a Javascript anti-pattern according to some other responses here, but I thought of doing something like this:

String.prototype._id = function () {
    return String.prototype.valueOf()
}

Regardless of how bad of an idea this is -already answered in other questions-, would this be a way of doing it, and where should I declare this? Is there a better way of solving this issue?

Thanks.

PS. I know I could also overwrite the toString method in my Address class and get a similar result, calling customer.address.toString() to get the _id, but I feel like it's not legible and I'd like to explore other options first.

RTYX
  • 1,244
  • 1
  • 14
  • 32
  • 1
    I still think you should not do it at all :-) But if you insist, you probably want a [getter](https://stackoverflow.com/questions/10592753/how-to-define-setter-getter-on-prototype) and not a function being added to `String.prototype`. – str Jan 21 '19 at 16:48
  • Thanks for the comment! I will most likely not do it, but I am still curious as what would be the best way and what would be the downsides. In the end, maybe I'll just create some sort of utility function. – RTYX Jan 21 '19 at 17:03

2 Answers2

0

Based on this and my experience I would advise your to put like this, if your application is AngularJS:

yourApp.run(function() { 
   String.prototype._id = function () {
     return String.prototype.valueOf()
   } 
}); 

For Angular 2+ you can check this and check this stackoverflow post that tells you how to extend String properties on Typescript.

Ricardo Rocha
  • 14,612
  • 20
  • 74
  • 130
0

You might like to consider using a Proxy which wraps your object and provides a means to catch missing values and much more.

The example below shows how the desired functionality can be achieved without having to extend any built-in types.

let customer1 = {
  address: 'some string'
}

let customer2 = {
  address: {
    _id: '12345'
  }
}


let proxyCustomer = customer => new Proxy(customer, {
  get: (target, name) => {
    // define the properties to catch and their functions
    let propHandlers = {
      address: () => {
        a = target.address
        // decide what you want to do
        // in this example a string value is returned as a dummy object with an _id.
        return typeof a === 'string' || a instanceof String ?
          {_id: a} : a
      }
    }

    // default action - return prop
    let def = () => target[name]

    // assign function to handleProp if it exists or use the default (return named prop)
    let handleProp = propHandlers[name] || def

    // return result of handle call
    return handleProp();
  }
})

// wrap customer objects in proxy
customer1 = proxyCustomer(customer1)
customer2 = proxyCustomer(customer2)

// test
console.log(customer1.address._id)
console.log(customer2.address._id)
John
  • 1,313
  • 9
  • 21