To better answer, let's start dropping any direct access and put a payload in the mix.
As example, in the `foo()?.bar.baz` case, you might end up having `null` or `undefined`, as result, because `foo().bar` existed, but `bar.baz` didn't. In the `foo()?.bar?.baz` case, you might end up having `foo().bar`, because `bar.baz` didn't exist. But what if you are not interested in the whole chain, but only in a main specific point in such chain? In that case you would have `foo()?.bar.baz ?? foo()`, but you wouldn't know how to obtain that via `foo()?.bar?.baz ?? foo()`, because the latest one might result into `foo().bar`. Moreover, in both cases you'll end up multiplying the payload at least * 2, while the mouse trap will work like this: ```js foo()<?.bar?.baz ``` if either `foo().bar` or `bar.baz` don't exist, the returned result is `foo()`, and it's computed once. You don't care about `foo().bar` if `bar.baz` is not there, 'cause you want to retrieve `foo()` whenever you have a failure down the chain. Specially with DB operations, this is a very common case (abstraction layers all have somehow different nested objects with various info) and the specific info you want to know is usually attached at the top level bject, while crawling its sub properties either leads to the expected result or you're left clueless about the result, 'cause all info got lost in the chain. The `foo()<?.bar.baz` case is a bit different though, 'cause if `foo().bar` existed, there's no way to expect `foo()` as result, and if it's `bar` that you're after you can write instead `foo()?.bar<?.baz` so that if `baz` is not there, `bar` it is. This short-circuit the need for `??` in most cases, 'cause you already point at the desired result in the chain in case the result would be `null` or `undefined`. However, `??` itself doesn't provide any ability to reach any point in the previous chain that failed, so that once again, you find yourself crawling such chain as fallback, resulting potentially in multiple chains and repeated payloads. ```js // nested chains foo()?.bar.baz?.biz ?? foo()?.bar.baz ?? foo()?.bar; // mouse trap foo()?.bar<?.baz?.biz; ``` Above example would prefer `foo().bar` if it exists, and if either `bar.baz` or `bar.baz.biz` returned `null` or `undefined`. I hope this clarifies further the intent, or the simplification, that such operator offers: it's a complementary hint for any optional chain, it doesn't have to be used, but when it does, it's visually semantic in its intent (at least to my eyes). Regards On Fri, Sep 6, 2019 at 11:20 PM Tab Atkins Jr. <jackalm...@gmail.com> wrote: > On Fri, Sep 6, 2019 at 8:04 AM Andrea Giammarchi > <andrea.giammar...@gmail.com> wrote: > > Indeed I'm not super convinced myself about the "branching issue" 'cause > `const result = this?.is?.branching?.already` and all I am proposing is to > hint the syntax where to stop in case something else fails down the line, > as in `const result = this.?.is<?.branching?.too` to know that if any other > part is not reached, there is a certain point to keep going (which is, > example, checking that `result !== this`) > > Important distinction there is that ?. only "branches" between the > intended type and undefined, not between two arbitrary types. The > cognitive load between those two is significantly different. > > In particular, you can't *do* anything with undefined, so > `foo?.bar.baz` has pretty unambiguous semantics - you don't think you > might be accessing the .baz property of undefined, because that > clearly doesn't exist. > > That's not the case with mouse, where it's not clear, at least to me, > whether `foo<?.bar.baz` is doing `(foo.bar ? foo.bar : foo).baz` or > `foo.bar.baz ? foo.bar.baz : foo` or even `foo.bar ? foo.bar.baz : > foo`. All three seem at least somewhat reasonable, and definitely > *believable* as an interpretation! > > ~TJ >
_______________________________________________ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss