Home > Software design >  Accessing Javascript class keys before class initialization
Accessing Javascript class keys before class initialization

Time:08-11

As I had a problem where my constructor became quite large and it started to become a problem maintaining it. I created this solution as a start of the refactor, however this still looked bulky. And it was easy to make a mistake.

    constructor(data: Partial<BusinessConfiguration>) {
    if(!data) return;
   
    this.logoId = data.logoId;
    this.positiveLogoId = data.positiveLogoId;
    this.negativeLogoId = data.negativeLogoId;
    this.faviconId = data.faviconId;
    this.primaryColor = data.primaryColor;
    this.windowTabName = data.windowTabName;
    this.shouldShowGDPR = data.shouldShowGDPR;
    this.allowsTermination = data.allowsTermination;
    this.styleConfiguration = data.styleConfiguration;
    this.shouldUseCustomTitle = data.shouldUseCustomTitle;
    this.processNotesStickers = data.processNotesStickers;
    this.terminationProcesses = data.terminationProcesses;
    this.shouldUseCustomHeader =data.shouldUseCustomHeader;
    this.completeDelete = data.completeDelete
    this.pingConfiguration = data.pingConfiguration;
    this.passwordConfiguration = data.passwordConfiguration;
    this.shouldAllowRandomRepeat = data.shouldAllowRandomRepeat;
    this.shouldRedirectImmediately = data.shouldRedirectImmediately;

}

So I created this, with this logic and good validation in my routes I never have to updated my constructor again. Whatever we send, those properties will be assigned. However, I am a bit unhappy with the fact that I am kind of brute forcing this. I want to check if the element from data, exists as a property of the class BusinessConfiguration.

    constructor(data: Partial<BusinessConfiguration>) {
    if(!data) return;
    
    Object.keys(data).forEach(element => {
        this[element] = data[element]
    });
   }

I had this problem on multiple occasions and maybe this is not the best example as I do have a type on data. But if data enters this constructor as type of any, mistakes and unwanted things may happen.

So bottom line is, How to access class property keys before object initialization.

I had an idea to create some kind of data structure which would hold all class property keys, but I hope there is something more clean. And if there is not, how would I go about making that as clean as possible, ideas are welcomed. Thank you in advance :)

CodePudding user response:

Rather than defining fields in the constructor, you can use public class fields:

class Business {
  // Defines a property but without a value (undefined).
  // You can add TypeScript types to these.
  logoId;
  positiveLogoId;
  negativeLogoId;
  // ...
}

Next, in the constructor you can include a loop that's similar to what you have, but with an extra filter:

class Business {
  logoId;
  positiveLogoId;
  negativeLogoId;

  constructor(data) {
    Object
      .keys(data)
      .filter(key => this.hasOwnProperty(key))
      .forEach((element) => {
        this[element] = data[element];
      });
  }
}

A different syntax, using the modern hasOwn and for...of:

class Business {
  logoId;
  positiveLogoId;
  negativeLogoId;

  constructor(data) {
    for (const [key, value] of Object.entries(data)) {
      if (Object.hasOwn(this, key)) {
        this[key] = value;
      }
    }
  }
}

Using either of the above, you can now only set properties that exist:

const b = new Business({
  logoId: 5,
  positiveLogoId: 10,
  foo: 1,
});

console.log(
  Object.keys(b).includes('foo')
); // false

CodePudding user response:

The best answer I could come up with is to have a static variable on the class where I would store property names, but then this array needs to be updated, which is not much different than updating the constructor, but I do get an added benefit where I can access the keys of this class for this kind of operations if I need.

     class Business {
     private static keys = ['logoId', 'positiveLogoId', 'negativeLogoId']

     logoId;
     positiveLogoId;
     negativeLogoId;

     constructor(data: Partial<Business>) {
       Object.keys(data).forEach((element) => {
        if (Business.keys.includes(element))
           this[element] = data[element];
  });
}
}
      

    const obj = new Business({
    logoId: 5,
    positiveLogoId: '10',
    foo: 1
  }as any)

    console.log(obj) // Business { logoId: 5, positiveLogoId: '10'}
  • Related