Home > Software design >  Specify what optional property to set in Typescript class constructor
Specify what optional property to set in Typescript class constructor

Time:09-21

I have a class that looks as follows:

export class Lifter {
id: string;
name: string;
weightclass: number;
bestSnatch: number;
bestCJ: number;
bestTotal: number;
bio: string;
imageURL: string;


constructor(id: string, name: string, weightclass:number, bestSnatch:number, bestCJ:number, bestTotal:number, bio="Default biography text", imageURL = "https://upload.wikimedia.org/wikipedia/commons/a/a0/Arh-avatar.jpg") {
    this.id = id;
    this.name = name;
    this.weightclass = weightclass;
    this.bestSnatch = bestSnatch;
    this.bestCJ = bestCJ;
    this.bestTotal = bestTotal;
    this.bio = bio;
    this.imageURL = imageURL;
    
}

}

So in the constructor I pass default values for the properties "bio" and "imageURL". This is working fine when I create a new Lifter by:

new Lifter("1","Jesper",73,107,125,220),

the default values for Bio and ImageURL are successfully being added. Now to my question.

Is it possible to enter a value for the imageURL-property without passing a value to the bio-property?

I would in some cases want to add just an ImageURL, sometimes just a bio, sometimes both of them and sometimes none of them. I hope the question and the problem is somewhat clear.

The different calls I would want to be functioning is as follows:

new Lifter("1","Jesper",73,107,125,220), //None of the properties
new Lifter("2","Alfred",83,187,205,220,"Some Biography"), //Biography passed
new Lifter("3","Manfred",73,107,125,220,"https://www.w3schools.com/howto/img_avatar2.png"),//ImageURL passed
new Lifter("4","Sigfred",73,107,125,220,"Some other biography" ,"https://www.w3schools.com/howto/img_avatar2.png"),//Values for both properties passed

For clarification, it's just the 3rd case above that I have a problem with.

CodePudding user response:

One way of doing this is through the object destructuring workaround for named parameters.

Your constructor destructures the object passed in as an argument:

constructor(id: string, name: string, weightclass:number, bestSnatch:number, bestCJ:number, bestTotal:number, {bio="Default bio", imageURL = "https://upload.wikimedia.org/wikipedia/commons/a/a0/Arh-avatar.jpg"} = {}) {
  this.id = id;
  this.name = name;
  this.weightclass = weightclass;
  this.bestSnatch = bestSnatch;
  this.bestCJ = bestCJ;
  this.bestTotal = bestTotal;
  this.bio = bio;
  this.imageURL = imageURL;
}

And you can call it like this by passing in appropriate objects:

new Lifter("1","Jesper",73,107,125,220)
new Lifter("2","Alfred",83,187,205,220,{bio: "Some Biography"})
new Lifter("3","Manfred",73,107,125,220,{imageURL: "https://www.w3schools.com/howto/img_avatar2.png"})
new Lifter("4","Sigfred",73,107,125,220,{bio: "Some other biography" , imageURL: "https://www.w3schools.com/howto/img_avatar2.png"})

CodePudding user response:

You could pass a null in for bio, or you could make it optional as stated above. bio has to have a value when you create a new instance of the class because that's the way that you've defined the class.

new Lifter("3","Manfred",73,107,125,220,null,"https://www.w3schools.com/howto/img_avatar2.png"),//ImageURL passed

CodePudding user response:

Besides already described ways there are two other ways I just wanted to mention:

  1. Static factory methods:
export class Lifter {
  id: string;
  name: string;
  weightclass: number;
  bestSnatch: number;
  bestCJ: number;
  bestTotal: number;
  bio: string = "Default biography text";
  imageURL: string = "https://upload.wikimedia.org/wikipedia/commons/a/a0/Arh-avatar.jpg";

  private constructor(id: string, name: string, weightclass:number, bestSnatch:number, bestCJ:number, bestTotal:number) {
    this.id = id;
    this.name = name;
    this.weightclass = weightclass;
    this.bestSnatch = bestSnatch;
    this.bestCJ = bestCJ;
    this.bestTotal = bestTotal;
  }

  static create(id: string, name: string, weightclass:number, bestSnatch:number, bestCJ:number, bestTotal:number, bio: string, imageURL: string): Lifter {
    return new Lifter(id, name, weightclass, bestSnatch, bestCJ, bestTotal);
  }

  static createWithDefaultBioAndImageURL(id: string, name: string, weightclass:number, bestSnatch:number, bestCJ:number, bestTotal:number): Lifter {
    return new Lifter(id, name, weightclass, bestSnatch, bestCJ, bestTotal);
  }

  static createWithDefaultBio(id: string, name: string, weightclass:number, bestSnatch:number, bestCJ:number, bestTotal:number, imageURL: string): Lifter {
    const lifter = new Lifter(id, name, weightclass, bestSnatch, bestCJ, bestTotal);
    lifter.imageURL = imageURL;
    return lifter;
  }

  static createWithDefaultImageURL(id: string, name: string, weightclass:number, bestSnatch:number, bestCJ:number, bestTotal:number, bio: string): Lifter {
    const lifter = new Lifter(id, name, weightclass, bestSnatch, bestCJ, bestTotal);
    lifter.bio = bio;
    return lifter;
  }
}

const lifter1 = Lifter.create("3","Manfred",73,107,125,220,"sampleBio", "https://www.w3schools.com/howto/img_avatar2.png");
const lifter2 = Lifter.createWithDefaultBio("3","Manfred",73,107,125,220,"https://www.w3schools.com/howto/img_avatar2.png");
const lifter3 = Lifter.createWithDefaultImageURL("3","Manfred",73,107,125,220,"sampleBio");
const lifter4 = Lifter.createWithDefaultBioAndImageURL("3","Manfred",73,107,125,220);

Example here

  1. Builder pattern

It can be implemented in several different ways, below is just sample one but you can also modify it in some ways, e.g. Lifter class could accept only LifterBuilder in constructor as a parameter and grab parameters from it - this way you could prevent initialization of this class without builder. The exact implementation depends on your needs and preferences.

export class Lifter {
  id: string;
  name: string;
  weightclass: number;
  bestSnatch: number;
  bestCJ: number;
  bestTotal: number;
  bio: string = "Default biography text";
  imageURL: string = "https://upload.wikimedia.org/wikipedia/commons/a/a0/Arh-avatar.jpg";

  constructor(id: string, name: string, weightclass:number, bestSnatch:number, bestCJ:number, bestTotal:number, bio: string, imageURL: string) {
    this.id = id;
    this.name = name;
    this.weightclass = weightclass;
    this.bestSnatch = bestSnatch;
    this.bestCJ = bestCJ;
    this.bestTotal = bestTotal;
  }
}

export class LifterBuilder {
  id: string;
  name: string;
  weightclass: number;
  bestSnatch: number;
  bestCJ: number;
  bestTotal: number;
  bio: string = "Default biography text";
  imageURL: string = "https://upload.wikimedia.org/wikipedia/commons/a/a0/Arh-avatar.jpg";

  constructor(id: string, name: string, weightclass:number, bestSnatch:number, bestCJ:number, bestTotal:number) {
    this.id = id;
    this.name = name;
    this.weightclass = weightclass;
    this.bestSnatch = bestSnatch;
    this.bestCJ = bestCJ;
    this.bestTotal = bestTotal;
  }

  withBio(bio: string): LifterBuilder {
      this.bio = bio;
      return this;
  }

  withImageURL(imageURL: string): LifterBuilder {
      this.imageURL = imageURL;
      return this;
  }

  build(): Lifter {
      return new Lifter(this.id, this.name, this.weightclass, this.bestSnatch, this.bestCJ, this.bestTotal, this.bio, this.imageURL);
  }
}

const lifter = new LifterBuilder(...).withBio(...).withImageURL(...).build(); // just call withBio or withImageURL when you need them and don't call any of them if you don't want to change default values

CodePudding user response:

bio?: string;
imageURL?: string;
  • Related