Primitive values from class constructors

# Raul-Sebastian Mihăilă (9 years ago)

I see that in step 13 of the [[Construct]] method of ordinary functions ( tc39.github.io/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget), the value resulted from the evaluation of the function body is checked and if the constructor is a derived constructor, the value cannot be a primitive different from undefined. Why isn't the same check performed if the constructor is a base constructor?

Example of code that, when executed in the Chrome console, throws:

(function () {
'use strict';

class A {
  constructor() {
    return 4;
  }
}

class B extends A {
  constructor() {
    super();

    return 2;
  }
}

new B();
})();
# Isiah Meadows (9 years ago)

Classes aren't intended to be value type factories, so I find this unlikely to change.

# Raul-Sebastian Mihăilă (9 years ago)

I agree, that's why I think it would have been better to throw if base constructors returned primitives different from undefined. It would have been inconsistent with the way normal old functions behave. But, currently, there are two other inconsistencies: there's an inconsistency between base class constructors and derived class constructors, and one between normal functions and derived class constructors. I would have preferred the first inconsistency (between base class constructors and normal functions).

# Isiah Meadows (9 years ago)

Although that is a weird Inconsistency. To clarify, which of these don't throw when Sub is called as a constructor? It's surprising IMHO if any don't.

// 1.
function Super() { return 2 }
class Sub extends Super {}

// 2.
class Super {
    constructor() { return 2 }
}
class Sub extends Super {}
# Raul-Sebastian Mihăilă (9 years ago)

None does.

# Isiah Meadows (9 years ago)

What about these? Presumably, both do?

// 1.
function Super() { return 2 }
class Sub extends Super {
    constructor() { super(); return 3 }
}

// 2.
class Super {
    constructor() { return 2 }
}
class Sub extends Super {
    constructor() { super(); return 3 }
}
# Raul-Sebastian Mihăilă (9 years ago)

Yes, both do.

# Isiah Meadows (9 years ago)

So, what about these?

// 1.
function Super() {}

class Sub extends Super {
    constructor() { super(); return 3 }
}

// 2.
class Super {}

class Sub extends Super {
    constructor() { super(); return 3 }
}
# Raul-Sebastian Mihăilă (9 years ago)

The rule is that derived constructors throw if they return a primitive different from undefined, so these throw.

# Isiah Meadows (9 years ago)

The thing is, I'd expect all of them to throw. I would rather that check be placed on the super call than the constructor, or just wholly ignored, with this being a primitive. That's IMHO an odd behavior for that edge case. I know there exist reasons to return primitives from constructors, but subclassing should be consistent with constructors in what they're allowed to return.

# Raul-Sebastian Mihăilă (9 years ago)

But note that this is not a primitive (not even in base constructors). So if a base constructor syntactically returns a primitive, it will semantically return its this value, which is an object. So the this value will be an object in the derived constructor as well.

# Isiah Meadows (9 years ago)

If I recall, it should be a primitive in the first two pairs I asked about. If that's not the case, that's a bug in the spec.

bugs.ecmascript.org

# Claude Pache (9 years ago)

Le 8 mars 2016 à 21:46, Raul-Sebastian Mihăilă <raul.mihaila at gmail.com> a écrit :

I see that in step 13 of the [[Construct]] method of ordinary functions (tc39.github.io/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget, tc39.github.io/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget), the value resulted from the evaluation of the function body is checked and if the constructor is a derived constructor, the value cannot be a primitive different from undefined. Why isn't the same check performed if the constructor is a base constructor?

I guess that the motivation was backward compatibility constraints for base constructors defined the legacy way, i.e., using the function keyword, where returning a primitive is equivalent to returning undefined.

But I agree that it would be better not to extend that sloppiness to base constructors defined using the class keyword.

I've opened: tc39/ecma262#469, tc39/ecma262#469