tc39 / proposal-class-fields

Orthogonally-informed combination of public and private fields proposals
https://arai-a.github.io/ecma262-compare/?pr=1668
1.72k stars 113 forks source link

Class private fields can't be used with `extends null` #288

Closed ExE-Boss closed 4 years ago

ExE-Boss commented 4 years ago

📝 See https://github.com/tc39/ecma262/issues/1036 and https://github.com/tc39/ecma262/pull/1321.


With public fields, you can at least do:

class Foo extends null {
    foo;
    constructor(foo) {
        return Object.create(new.target.prototype, {
            foo: {
                configurable: true,
                enumerable: true,
                writable: true,
                value: foo,
            },
        });
    }
}

But with private fields, you can’t use the Object.create work‑around.


It’d be so much easier if:

class Foo extends null {};
new Foo;

and

class Foo extends null {
    constructor() {}
};
new Foo;

didn’t throw.


That would allow class fields to also be used with extends null:

class Foo extends null {
    #foo;
    constructor(foo) {
        this.#foo = foo;
    }
}
new Foo;
ljharb commented 4 years ago

This is a result of classes themselves not properly supporting null. You can, however, extend a class that extends null and uses your workaround.

rdking commented 4 years ago

What's the glitch in the null support?

ExE-Boss commented 4 years ago

The issue is that a constructor that extends null ends up with its [[ConstructorKind]] internal slot set to derived instead of base, which results in the implicit constructor being constructor(...args) { super(...args); } and the this binding being uninitialized instead of OrdinaryCreateFromConstructor(newTarget, "%Object.prototype%").

This is being fixed in issue https://github.com/tc39/ecma262/issues/1036 (PR: https://github.com/tc39/ecma262/pull/1321).

rdking commented 4 years ago

I get it. Now just out of curiosity, why was class designed that way? It seems like it would've made things cleaner for there to never be a [[ConstructorKind]], for all actions to happen as if "derived" were the only kind, for the class base to always be either Object, null, or user specified, and for super() to be an internal function that automatically handles the special case of a null base. So why split the situation into something that requires 2 different constructor types?

rdking commented 4 years ago

Just thought about it. That's questions better targeted on es-discuss...

littledan commented 4 years ago

Yeah, classes extending null is sort of generally broken, unfortunately. See https://github.com/tc39/ecma262/pull/1321 for some further discussion. Closing this issue, as it's not about class fields.