School Catalogue

Hi everyone,

I’m working through the School Catalogue project in the Learn Intermediate JavaScript course, and have stumbled onto a bit of a roadblock.

The PrimarySchool instance (on Line 71) is passing a string to the PrimarySchool class constructor (Line 47), which in turn passes it up to the School class constructor (Line 3) to set the numberOfStudents property. However, it doesn’t seem to trigger the console message on Line 25 which is meant to determine if the input is invalid (i.e: not a number).

Any idea what’s going on here?

Apologies in advance for any miscommunication or incorrect terms used.

// Parent Class
class School {
  constructor(name, level, numberOfStudents) {
    this._name = name;
    this._level = level;
    this._numberOfStudents = numberOfStudents;
  }

  // Getters
  get name() {
    return this._name;
  }

  get level() {
    return this._level;
  }

  get numberOfStudents() {
    return this._numberOfStudents;
  }

  // Setters
  set numberOfStudents(number) {
    if (number.isNaN()) {
      console.log("Invalid input: numberOfStudents must be set to a number.");
    } else {
      this._numberOfStudents = number;
    }
  }

  quickFacts() {
    console.log(
      `${this.name} educates ${this.numberOfStudents} students at the ${this.level} school level.`
    );
  }

  static pickSubstituteTeacher(substituteTeachers) {
    const randomNumber = Math.floor(
      Math.random() * (substituteTeachers.length - 1)
    );
    return substituteTeachers[randomNumber];
  }
}

// Primary School Class
class PrimarySchool extends School {
  constructor(name, numberOfStudents, pickupPolicy) {
    super(name, "primary", numberOfStudents);
    this._pickupPolicy = pickupPolicy;
  }

  // Getters
  get pickupPolicy() {
    return this._pickupPolicy;
  }
}

// High School Class
class HighSchool extends School {
  constructor(name, numberOfStudents, sportsTeams) {
    super(name, "high", numberOfStudents);
    this._sportsTeams = sportsTeams;
  }

  // Getters
  get sportsTeams() {
    return this._sportsTeams;
  }
}

// Primary School Creation
const lorraineHansbury = new PrimarySchool(
  "Lorraine Hansbury",
  "Not a Number",
  "Students must be picked up by a parent, guardian, or a family member over the age of 13."
);

lorraineHansbury.quickFacts();

// High School Creation
const alSmith = new HighSchool("Al E. Smith", 415, [
  "Baseball",
  "Basketball",
  "Volleyball",
  "Track and Field",
]);

console.log(alSmith.sportsTeams);

// Static Method
const substitute = School.pickSubstituteTeacher([
  "Jamal Crawford",
  "Lou Williams",
  "J. R. Smith",
  "James Harden",
  "Jason Terry",
  "Manu Ginobli",
]);

In the numberOfStudents setter for the School class,
I think that should be
isNaN(number)
or
Number.isNaN(number)

otherwise it’ll just check the .isNaN property of the number variable;
and if it doesn’t have one, number.isNaN would be undefined (which would work like false in that if-statement condition).

Hmm, no luck with either of these.

The odd thing is, the setup is nearly completely identical to the video guide.

Is there something which has been overlooked here?

EDIT: It appears that the following code works, although I’m not so sure why removing the prepended '_' in the School constructor solves this. I also changed the setter to check for typeof instead.

// Parent Class
class School {
  constructor(name, level, numberOfStudents) {
    this._name = name;
    this._level = level;
    this.numberOfStudents = numberOfStudents;
  }

  // Getters
  get name() {
    return this._name;
  }

  get level() {
    return this._level;
  }

  get numberOfStudents() {
    return this._numberOfStudents;
  }

  // Setters
  set numberOfStudents(number) {
    if (typeof number === 'string') {
      this._numberOfStudents = '[UNDEFINED]';
      console.log("Invalid input: numberOfStudents must be set to a number.");
    } else {
      this._numberOfStudents = number;
    }
  }

  quickFacts() {
    console.log(
      `${this.name} educates ${this.numberOfStudents} students at the ${this.level} school level.`
    );
  }

  static pickSubstituteTeacher(substituteTeachers) {
    const randomNumber = Math.floor(
      Math.random() * (substituteTeachers.length - 1)
    );
    return substituteTeachers[randomNumber];
  }
}

// Primary School Instance
class PrimarySchool extends School {
  constructor(name, numberOfStudents, pickupPolicy) {
    super(name, "primary", numberOfStudents);
    this._pickupPolicy = pickupPolicy;
  }

  // Getters
  get pickupPolicy() {
    return this._pickupPolicy;
  }
}

// High School Instance
class HighSchool extends School {
  constructor(name, numberOfStudents, sportsTeams) {
    super(name, "high", numberOfStudents);
    this._sportsTeams = sportsTeams;
  }

  // Getters
  get sportsTeams() {
    return this._sportsTeams;
  }
}

// Primary School Creation
const lorraineHansbury = new PrimarySchool(
  "Lorraine Hansbury",
  "Not a Number",
  "Students must be picked up by a parent, guardian, or a family member over the age of 13."
);

lorraineHansbury.quickFacts();

// High School Creation
const alSmith = new HighSchool("Al E. Smith", 415, [
  "Baseball",
  "Basketball",
  "Volleyball",
  "Track and Field",
]);

console.log(alSmith.sportsTeams);

// Static Method
const substitute = School.pickSubstituteTeacher([
  "Jamal Crawford",
  "Lou Williams",
  "J. R. Smith",
  "James Harden",
  "Jason Terry",
  "Manu Ginobli",
]);

The issue seems to be the constructor, not the setter.

details

You set up a new instance of PrimarySchool here:

const lorraineHansbury = new PrimarySchool(
  "Lorraine Hansbury",
  "Not a Number",
  "Students must be picked up by a parent, guardian, or a family member over the age of 13."
);

This calls the constructor for PrimarySchool, not a setter for ._numberOfStudents
And the constructor for PrimarySchool uses the constructor for School.

You could check whether that argument/parameter is a number in the constructor.

class School {
  constructor(name, level, numberOfStudents) {
    this._name = name;
    this._level = level;
    if ((typeof numberOfStudents) == "number") {
      this._numberOfStudents = numberOfStudents;
    }
    else {
      this._numberOfStudents = 0;
    }
  }
1 Like

Thanks a bunch, this worked!

Do you know why the setter would have been used in this guide if it ultimately just calls the constructor? I think I’m still a little lost on that.

I don’t know why.
Maybe they just didn’t want to make the constructors more complicated. I donno.

I guess you could check if the argument is a number in the setter too.

  set numberOfStudents(number) {
    if ((typeof numberOfStudents) == "number") {
      this._numberOfStudents = number;
    }
  }

You could even go further and check that its a number and not negative.

So this works, but only if I remove the prepended '_' from this_numberOfStudents in the School constructor:

// Parent Class
class School {
  constructor(name, level, numberOfStudents) {
    this._name = name;
    this._level = level;
    this.numberOfStudents = numberOfStudents;
  }

  // Getters
  get name() {
    return this._name;
  }

  get level() {
    return this._level;
  }

  get numberOfStudents() {
    return this._numberOfStudents;
  }

  // Setters
  set numberOfStudents(number) {
    if (typeof number === 'string') {
      this._numberOfStudents = '[invalid input]';
      console.log("Invalid input: numberOfStudents must be set to a number.");
    } else {
      this._numberOfStudents = number;
    }
  }

  quickFacts() {
    console.log(
      `${this.name} educates ${this.numberOfStudents} students at the ${this.level} school level.`
    );
  }

  static pickSubstituteTeacher(substituteTeachers) {
    const randomNumber = Math.floor(
      Math.random() * (substituteTeachers.length - 1)
    );
    return substituteTeachers[randomNumber];
  }
}

// Primary School Class
class PrimarySchool extends School {
  constructor(name, numberOfStudents, pickupPolicy) {
    super(name, "primary", numberOfStudents);
    this._pickupPolicy = pickupPolicy;
  }

  // Getters
  get pickupPolicy() {
    return this._pickupPolicy;
  }
}

// High School Class
class HighSchool extends School {
  constructor(name, numberOfStudents, sportsTeams) {
    super(name, "high", numberOfStudents);
    this._sportsTeams = sportsTeams;
  }

  // Getters
  get sportsTeams() {
    return this._sportsTeams;
  }
}

// Primary School Creation
const lorraineHansbury = new PrimarySchool(
  "Lorraine Hansbury",
  "Not a Number",
  "Students must be picked up by a parent, guardian, or a family member over the age of 13."
);

lorraineHansbury.quickFacts();

// High School Creation
const alSmith = new HighSchool("Al E. Smith", 415, [
  "Baseball",
  "Basketball",
  "Volleyball",
  "Track and Field",
]);

console.log(alSmith.sportsTeams);

// Static Method
const substitute = School.pickSubstituteTeacher([
  "Jamal Crawford",
  "Lou Williams",
  "J. R. Smith",
  "James Harden",
  "Jason Terry",
  "Manu Ginobli",
]);

Once I add it back in, it stops working, which is another point of confusion for me.

It might the case that
this.numberOfStudents
calls the setter
whereas
this._numberOfStudents
does not.