a "use set" directive to reverse the effects of class fields [[Define]]?

I use classes all day every day. As much as I want to love the syntax, sometimes I hate it. The [[Define]] semantics of class fields often leads to inevitably ugly code like the following:

class Foo extens Base {
  /** @override */
  foo = 123
}

// Ugh, put it on the prototype too, because the Base class might read it during construction.
Foo.prototype.foo = 123

Class fields with [[Define]] semantics are a mess.

// Ugh, put it on the prototype too, because the Base class might read it during construction.

Define vs Set has nothing to do with whether the field is initialized before the base class constructor runs.

Oops, you're right, I got confused. The whole state of class properties is confusing. :)

"Derived class constructors run after base class constructors" was the behavior prior to class fields as well (and is also true in other languages, for that matter). It's a pain point in other languages as well, but there's not much to be done about it; those two things have to happen in some defined order, and it's usually going to be incoherent to run the derived class constructor first.

The pattern I would probably use is, if the base class is OK with part of it's state being set by the subclass during initialization, then that should be part of it's (potentially optional) constructor arguments - so it is explicitly part of the relationship.

class Foo extends Base {
   constructor() {
     super({ foo: 123 });
   }
}
2 Likes

If we were working with the more powerful 1.0 decorators proposal in ES, I'd rather have 2 decorators to handle this:

  • @usePrototype - class or field-level decorator that initializes all covered fields on the prototype at the time of definition. (Obviously [[Define]] semantics.)
  • @useSet - class or field-level decorator that ensures all covered fields are initialized using [[Set]] semantics on the instance. (Overrides initialization from @usePrototype.)

Sadly, the new watered down wrapper decorators cannot be used to implement this.