Should “optional new” be implemented?
claudepache opened this issue · 11 comments
Apart the absence of use cases, I have the following additional arguments against it:
- A complication in the mental model for short-circuiting due to non-strict left-to-right evaluation order of
new
. The simple rule for short-circuiting is: “if the LHS of the operator is null, do not evaluate its RHS”. However, in expressions likenew a?.()
andnew a?.b()
, ifa
is null, thenew
operator will not be evaluated, even if the keyword is found on the left of?
. - Technically, a complication in the grammar, due to the coexistence of argument-less constructors
new a
and function invocationsb()
with similar precedence. That would force to duplicate the rules for OptionalExpression, in the same way that the current spec has both MemberExpression and CallExpression.
So, I’m closing this issue, deciding to drop support for new
. But if you have objections, you can still share them in comments.
@claudepache Sorry to bring up an old discussion that didn't seem to get much attention, but:
Feel free to tell me if this makes no sense, but if there's a use case for function invocation:
func?.(); // is the func non-null? call it.
Just from a naive point of view, there would also be a use case for creating an instance with new
(as they're both kind of just function calls):
new Class?.(); // is the Class non-null? Create an instance.
However, I can definitely understand if this is not supported due to new undefined
throwing TypeError: undefined is not a constructor
(in the case where Class is null/undefined).
On the other hand, with the introduction of null coalescing, there's this possible case (admittedly a weird way of writing it):
// flag.js
const someCondition = 1 === 1;
module.exports = () => someCondition; // export some condition that evaluates to true
// NonNullish.js
const flag = require('./flag')();
class NonNullish {
// ...
}
module.exports = flag ? NonNullish : null; // if flag is set, export NonNullish
// Nullish.js
const flag = require('./flag')();
class Nullish {
//...
}
module.exports = flag ? null : Nullish; // if flag is set, don't export Nullish
// instance.js
const NonNullish = require('./NonNullish');
const Nullish = require('./Nullish');
let instance;
if (Nullish != null)
instance = new Nullish();
else
instance = new NonNullish();
// when flag is set to true:
instance instanceof NonNullish // true
// when flag is set to false:
instance instanceof Nullish // true
The example ended up being a bit more lengthy than I first envisioned it, sorry. 😂
But with support for the new
keyword and null coalescing, we could replace:
let instance;
if (Nullish != null)
instance = new Nullish();
else
instance = new NonNullish();
with
const instance = new Nullish.?() ?? NonNullish();
On the other hand, I may feel more strongly than others on the importance of "uniformity of treatment" as you mentioned in Issue #10:
For the sake of uniformity of treatment, I think one should include what the spec calls “Left-Hand-Side Expression”, which includes additionally
new
and tagged templates (which are sort of method/function invocations), e.g.new a?b.c().d[x] `{y}`;
I actually feel more strongly about support for tagged template literals, but that is a discussion for another time/place.
Realized shortly after posting that you can currently write this:
const instance = new (Nullish || NonNullish)();
and I'd expect this to be possible w/ null coalescing:
const instance = new (Nullish ?? NonNullish)();
but I'd think this should also be available (perhaps a bit more idiomatic to some):
const instance = new Nullish?.() ?? NonNullish)();
const instance = new (Nullish || NonNullish)();
That's what I am currently doing:
throw new (self[type] || self.Error)(msg);
I didn't feel the need of anything fancy. Furthermore ?[]
is not valid cf #5.
I didn't feel the need of anything fancy. Furthermore
?[]
is not valid cf #5.
Sorry @Mouvedia, not sure what you mean. I realize there are problems with ?[
and ?(
. How does that apply here?
I'm honestly not sure how you're using this, but it seems it's another use case! Would it be helpful for clarity if
throw new (self[type] || self.Error)(msg);
could also be written
throw new (self?.[type] ?? self?.Error ?? Error)(msg);
in case of self === undefined
?
How does that apply here?
Because my example features brackets: self[type]
.
Would it be helpful for clarity if
throw new (self[type] || self.Error)(msg);could also be written
throw new (self?.[type] ?? self?.Error ?? Error)(msg);in case of
self === undefined
?
Sure, it can. (There is no “optional new”, here.)
The use case for ”optional new” should be sufficiently strong in order to justify the significant complication needed to specify it.
Thanks, I feel better about this direction now. 👍
Would it be helpful for clarity if
throw new (self[type] || self.Error)(msg);could also be written
throw new (self?.[type] ?? self?.Error ?? Error)(msg);in case of
self === undefined
?Sure, it can. (There is no “optional new”, here.)
The use case for ”optional new” should be sufficiently strong in order to justify the significant complication needed to specify it.
What's the meaning of the double question marks ??
?
What's the meaning of the double question marks
??
?
What's the meaning of the double question marks
??
?
Thanks for your quick reply, I found it too.