Closed aarondcohen closed 1 year ago
The point is to handle cases where property access would throw (which is only null
and undefined
); this isn’t the case for NaN.
It's not really clear why this proposal is tightly linked to property access (other than they both have ?
in the operator). A better guide is the ternary operator for x ? x : y
.
The property access operator ?.
essentially lifts the left side of the operator into the optional monad to allow for member access chaining (similar to &.
in ruby or ?.
in swift/c#). Presumably the end result of a chain where no value can be found is undefined
.
The null-ish operator (similar to //
in perl or the null coalescing operator in other languages) is about distinguishing falsey values due to an invalid state from falsey values that result from valid operations. The primary use of these operators is to make it easy to create defaults for input data. I have never known a use case where someone purposefully set a value to NaN
.
The null-ish operator (similar to // in perl or the null coalescing operator in other languages) is about distinguishing falsey values due to an invalid state from falsey values that result from valid operations.
The nullish coalescing operator is not able to distinguish falsy values according to their origin (invalid state vs. valid operation), as there is no objective way to distinguish between a ”valid” and an ”invalid” NaN, for instance.
The purpose of nullish coalescing is primarily to provide a default or a fallback value when ”no value” is provided, where the notion of ”no value” is concretised by the value null
.
But that doesn't match the motivating numerical example:
const animationDuration = response.settings?.animationDuration || 300; // Potentially unintended. 0 evaluates to false, result: 300
// vs
const animationDuration = response.settings?.animationDuration ?? 300;
In this case, we're determining which operator to use based on context. The assumption is the chain resolves successfully, but we want to preserve the value at the end of the chain.
animationDuration | `\ | \ | ` | ?? |
---|---|---|---|---|
undefined | 300 | 300 | ||
null | 300 | 300 | ||
0 | 300 | 0 | ||
NaN | 300 | ? |
It makes far more sense for NaN
to resolve to 300
than to stay NaN
There is a fundamental difference between null and NaN, though:
The null-coalescing operator should not be designed to be the numeric equivalent of the ”empty catch clause” for exceptions.
@aarondcohen It seems all languages which have null coalescing operator treat NaN as non-null (I tested C#, Swift, Kotlin, perl). So I think we'd better keep the same semantic.
I have been using the null coalescing operators for quite some time in CoffeeScript. It's just ?
there, but has the same semantics as this proposal.
I also remember being surprised that ?
doesn't branch on truthy/falsey values. When you refactor from if
or tertiary operator to the null coalescing operator, this is something a developer might expect.
However if we did this, then we would also need pass on false
, which should always considered a "given" value when setting defaults. Passing on false
would lead to bugs. Hence I recommend to keep the semantics of this proposal.
All this said, I do wish there was something that could coalesce NaN values specifically, as it's awkward to compress isNaN(Number(some long expression)) ? undefined : Number(some long expression)
or some long expression ? Number(some long expression) : undefined
; it basically requires 2 lines of code to not repeat some long expression
.
Closing, since this proposal is at stage 4.
If the point of this operator is to replace data in a likely invalid state with data in a valid state, then we should cover all javascript constants that typically represent invalid data. In that case: