Destructuring an optionally chained object, to get the ...rest? - javascript

I have this setup
const { discard_me, ...rest } = some?.optional?.chaining;
I end up with an error that discard_me doesn't exist, but that's expected if chaining is also non-existent. It seems like the optional chaining should cover the concerns of both the left and right hand side of the assignment.
Is there a way round this without nullish coalescing?

It seems like the optional chaining should cover the concerns of both
the left and right hand side of the assignment.
It doesn't, because some?.optional?.chaining is either going to resolve to:
some.optional.chaining (which may be undefined); or
undefined (which definitely is).
For the destructuring assignment, the right-hand side must be an object.

Related

What does the myObj.param?.otherParam operator do and how is it used?

I was reading the fundamentals guide for React Navigation and in the section for passing parameters to routes I came across this bit of code that I've never seen before.
if (route.params?.post) {
// Do something
}
I've never seen the ? operator used that way, I've only used the ternary operator. When searching the only other thing I found is the nullish assignment operator ??=.
I fiddled with it in the console and it seems to check if param exists so that if it doesn't exist it doesn't error when asking for .post
My first thought was that is a ternary operator without the second argument, but the third argument appears to be required.
So my question is, in the above code block, what is the ? doing, what is that called, and how/when is it used?
Thanks
It's called optional chaining. You can use it to check if the preceding variable is null, so that you could spare yourself checking for null/undefined properties.
// want to access blub.test.smth
if(blub && blub.test) {
// possibly access blub.test.smth
const value = blub.test.smth;
}
vs
const value = blub?.test?.smth

Optional-chaining "all to the right"?

For deeply nested property access, is there a JS operator similar to the "optional chaining" operator which applies to all properties/methods/etc. to the right?
Consider an object with deep nesting, where some properties are null:
const sort = payload.meta.displayConfig.properties.search.sort;
// ^---null? ^---null? ^--null?
This can be handled with the optional-chaining operator:
const sort = payload.meta.displayConfig?.properties?.search?.sort;
But is there another operator where "all calls to the right" are handled as if they were preceded by the optional-chaining operator? Observe:
const sort = payload.meta.displayConfig?!.properties.search.sort;
// ^^---fictional nested-optional-chaining
// operator
In this example (with the fictional ?! nested-optional-chaining operator), if anything from displayConfig and onward to the right (properties, search) are null or undefined, then execution is short-circuited as if each property was preceded by ?.
Is there any discussion around adding such a feature?
The short answer is no. You have to put ?. between each segment if you want to make each segment safe against null/undefined values. And as far as I can tell from looking through the different proposals for ecmascript there hasn't been any discussion of an operator like you're talking about.
Before the optional chaining operator was a thing, many libraries would implemented their own ways to get an attribute in a safe way, some of which behave closer to what you're wanting. For example, in lodash you can do _.get(payload, 'meta.displayConfig.properties.search.sort') However, now that the optional chaining operator is a thing, I would prefer just using it between every segment instead of using these library functions.

What does this JavaScript expression mean?

I am working with react-navigation and I can't figure out the meaning of this syntax.
React.useEffect(() => {
if (route.params?.post) { <<<<<WHAT IS THIS ?
// Post updated, do something with `route.params.post`
// For example, send the post to the server
}
}, [route.params?.post]);
Does it work like obect.doesPropertyExist.subProperty or something else?
I have tried going through MDN documentation but can't find any reference to this type of syntax. I am not able to use similar syntax on a random object in my node REPL.
It is called Optional Chaining. Provided in MDN doc:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
The optional chaining operator (?.) permits reading the value of a
property located deep within a chain of connected objects without
having to expressly validate that each reference in the chain is
valid. The ?. operator functions similarly to the . chaining operator,
except that instead of causing an error if a reference is nullish
(null or undefined), the expression short-circuits with a return value
of undefined. When used with function calls, it returns undefined if
the given function does not exist.
if(route.params.post) then do something.
It checks if route.params has object post
It's called Optional Chaining operator. And it's shorthand for
route.params && route.params.post

Unconditional use of conditional expression for default assignment

I'm getting the following linting error: Unconditional use of conditional expression for default assignment
What is wrong with the below?
(myOverride) ? myOverride : MAGIC_HOST,
Where if myOverride is defined I want to use myOverride, if it is not defined I want to use the env var MAGIC_HOST.
Apparently you're using ESLint (as that error is an ESLint) error. It's because of the no-unneeded-ternary rule which is meant to flag up unnecessary use of the conditional operator (they call it the "ternary")¹. From the linked docs:
Another common mistake is using a single variable as both the conditional test and the consequent. In such cases, the logical OR can be used to provide the same functionality. Here is an example:
// Bad
var foo = bar ? bar : 1;
// Good
var foo = bar || 1;
So the rule is telling you to use myOverride || MAGIC_HOST instead.
You don't have to, the code you've shown isn't wrong. It's just it doesn't pass that ESLint rule.
¹ "they call it the 'ternary'" - The conditional operator is a ternary operator (an operator accepting three operands, just like * is a binary operator — an operator accepting two operands). And it is, for now, the only ternary operator JavaScript has. But that doesn't necessarily always have to be true as the language evolves. It's correctly called the conditional operator.
It is not inherently wrong, but it is better written as:
myOverride || MAGIC_HOST
as explained here.

Why is `(foo) = "bar"` legal in JavaScript?

In Node.js's REPL (tested in SpiderMonkey as well) the sequence
var foo = null;
(foo) = "bar";
is valid, with foo subsequently equal to "bar" as opposed to null.
This seems counterintuitive because one would think the parenthesis would at least dereference bar and throw Invalid left-hand side in assignment`.
Understandably, when you do anything interesting it does fail in aforementioned way.
(foo, bar) = 4
(true ? bar : foo) = 4
According to ECMA-262 on LeftHandExpressions (so far as I can interpret) are no valid non-terminals that would lead to a parenthetical being accepted.
Is there something I'm not seeing?
It's valid indeed. You're allowed to wrap any simple assignment target in parenthesis.
The left hand part of the = operation is a LeftHandSideExpression as you correctly identified. This can be tracked down through the various precendence levels (NewExpression, MemberExpression) to a PrimaryExpression, which in turn might be a Cover­Parenthesized­Expression­And­Arrow­Parameter­List:
( Expression[In, ?Yield] )
(actually, when parsed with target PrimaryExpression, it's a ParenthesizedExpression).
So it's valid by the grammar, at least. Whether it's actually valid JS syntax is determined by another factor: early error static semantics. Those are basically prose or algorithmic rules that make some production expansions invalid (syntax errors) in certain cases. This for example allowed the authors to reuse the array and object initialiser grammars for destructuring, but only applying certain rules. In the early errors for assignment expressions we find
It is an early Reference Error if LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral and IsValidSimpleAssignmentTarget of LeftHandSideExpression is false.
We can also see this distinction in the evaluation of assignment expressions, where simple assignment targets are evaluated to a reference that can be assigned to, instead of getting the destructuring pattern stuff like object and array literals.
So what does that IsValidSimpleAssignmentTarget do to LeftHandSideExpressions? Basically it allows assignments to property accesses and disallows assignments to call expressions. It doesn't state anything about plain PrimaryExpressions which have their own IsValidSimpleAssignmentTarget rule. All it does is to extract the Expression between the parentheses through the Covered­Parenthesized­Expression operation, and then again check IsValidSimpleAssignmentTarget of that. In short: (…) = … is valid when … = … is valid. It'll yield true only for Identifiers (like in your example) and properties.
As per #dlatikay's suggestion, following an existing hunch, research into CoveredParenthesizedExpression yielded a better understanding of what's happening here.
Apparently, the reason why a non-terminal cannot be found in the spec, to explain why (foo) is acceptable as a LeftHandExpression, is surprisingly simple. I'm assuming you understand how parsers work, and that they operate in two separate stages: Lexing and Parsing.
What I've learned from this little research tangent is that the construct (foo) is not technically being delivered to parser, and therefor the engine, as you might think.
Consider the following
var foo = (((bar)));
As we all know, something like this is perfectly legal. Why? Well you visually can just ignore the parenthesis while the statement remains making perfect sense.
Similarly, here is another valid example, even from a human readability perspective, because the parentheses only explicate what PEMDAS already makes implicit.
(3 + ((4 * 5) / 2)) === 3 + 4 * 5 / 2
>> true
One key observation can be loosely derived from this, given an understanding of how parsers already work. (remember, Javascript still is being parsed (read: compiled) and then run) So in a direct sense, these parentheses are "stating the obvious".
So all that being said, what exactly is going on?
Basically, parentheses (with the exception of function parameters) are collapsed into proper groupings of their containing symbols. IANAL but, in lay man's terms, that means that parentheses are only interpreted to guide the parser how to group what it reads. If the context of the parentheses is already "in order", and thus does not require any tweaking of the emitted AST, then the (machine) code is emitted as if those parentheses did not exist at all.
The parser is more or less being lazy, assuming the parens are impertinent. (which in this edge-case, is not true)
Okay, and where exactly is this happening?
According to 12.2.1.5 Static Semantics: IsValidSimpleAssignmentTarget in the spec,
PrimaryExpression: (CoverParenthesizedExpressionAndArrowParameterList)
Let expr be CoveredParenthesizedExpression of CoverParenthesizedExpressionAndArrowParameterList.
Return IsValidSimpleAssignmentTarget of expr.
I.E. If expecting primaryExpression return whatever is inside the parenthesis and use that.
Because of that, in this scenario, it does not convert (foo) into CoveredParenthesizedExpression{ inner: "foo" }, it converts it simply into foo which preserves the fact that it is an Identifier and thus syntactically, while not necessarily lexically, valid.
TL;DR
It's wat.
Want a little more insight?
Check out #Bergi's answer.

Categories

Resources