Setters and Class Constructors

Hi everyone,

I did the ‘School Catalog’ project (https://www.codecademy.com/courses/learn-intermediate-javascript/projects/school-catalog) from the ‘Classes’ class and faced a problem which hasn’t been dealed in the course…

It is asked:

Create a setter for numberOfStudents. The method should first check if the input (newNumberOfStudents) is a number.
If it is a number, then set the numberOfStudents property. If not, log, 'Invalid input: numberOfStudents must be set to a Number.'

I created this setter and it does nothing… If I create an instance of School or any subclass and put a string or other as argument for ‘numberOfStudents’ it still takes it…
I asked ChatGPT why it doesn’t work and he told me that the setter will only work if you attempt to reassign the value of the property. He advised the following:

So, if you want to enforce type checking when initially creating a School instance, you can add type checking logic in the constructor itself, like this:

So what is the point then to use a setter with a class constructor if it doesn’t check at the instantiation but at a property reassignment ? Is it a mistake in the instruction of the project ?

Thank you all for your enlightment !

Setters, while they may be invoked during the process, are not a side effect of instantiation. They are methods inherited by the class instance.

We need the constructor to properly define them, and it may well be that some of the instance variables actually call them (we won’t go there) but they are literally simple constructs component with the instance object, aka, methods, albeit it special in nature. That’s the syntactic sugar that JS employed to amuse us, and to also so do side work.

class Foo {
  constructor (bar, baz) {
    this._bar = bar;
    this._baz = baz;
  }
  get bar () {
    return this._bar;
  }
  get baz () {
    return this._baz;
  }
  set bar (x) {
    if (typeof x === 'number') {
      this._bar = x;
    } else {
      console.log("illegal value");
    }
  }
  set baz (x) {
    if (typeof x === 'number') {
      this._baz = x;
    } else {
      console.log("illegal value");
    }
  }
}

Pay particular note to the fact that initial inputs are not type tested, only subsequent inputs. The initial inputs might be any type. It won’t matter if the intention is to change them immediately/eventually, anyway.

 > foo = new Foo (6, 7)
<- Foo {_bar: 6, _baz: 7}
 > foo.bar
<- 6
 > foo.baz
<- 7
 > foo.bar = "6"
   illegal value
<- '6'
 > foo.baz = "7"
   illegal value
<- '7'
 > 

This iteration is whole 'nother discussion, but here it is,

class Foo {
  constructor (bar, baz) {
    this._bar = bar;
    this._baz = baz;
  }
  get bar () {
    return this._bar;
  }
  get baz () {
    return this._baz;
  }
  set bar (x) {
    if (typeof +x == 'number') {
      this._bar = +x;
    } else {
      console.log("illegal value");
    }
  }
  set baz (x) {
    if (typeof +x == 'number') {
      this._baz = +x;
    } else {
      console.log("illegal value");
    }
  }
}
 > foo = new Foo (6, 7)
<- Foo {_bar: 6, _baz: 7}
 > foo.bar = "7"
<- '7'
 > foo.baz = "6"
<- '6'
 > foo.bar
<- 7
 > foo.baz
<- 6
 > 
class Foo {
  constructor (bar, baz) {
    this._bar = bar;
    this._baz = baz;
  }
  get bar () {
    return this._bar;
  }
  get baz () {
    return this._baz;
  }
  set bar (x) {
    if (isNaN(x)) {
      console.log("illegal value", x);
    } else {
      this._bar = +x;
    }
  }
  set baz (x) {
    if (isNaN(x)) {
      console.log("illegal value", x);
    } else {
      this._baz = +x;
    }
  }
}

Thanks for your answer!

1 Like