It's nice for situations where you want to access a deeply nested prop, and you only care whether the whole path is there or not. Saves you having to add a seperate check for every level of the hierarchy.
e.g. You can do:
foo?.bar?.baz || "default";
Rather than:
(foo && foo.bar && foo.bar.baz) || "default";
Agree that developers can be careful about nullability (in fact I pulled someone up on this in a code review earlier today), but I don't think this feature makes that any worse.
The problem is that if you find yourself needing deep accessors, something is very wrong with your scopes. You are reaching across many levels of concerns which is a code smell.
So, by making it “nice” you are making a code smell less smelly, which feels good in the moment, at the syntax level, but makes your code worse at the architecture level.
This is roughly the story for all of ES6... make it “nice” to work with bad code, allowing bad code to look more similar to good code, until everything looks “nice” at the syntax level but you are surrounded by footguns that are impossible to find, and you need more and more static analysis tools (like TypeScript) to even be able to comprehend your control structures.
Callback hell isn’t bad because of indentation, it’s bad because there are too many handoffs in a small space. Promises make it easier to pack more handoffs into a small space, and guess what? Now the problem is even worse.
This new ? operator will make it easier than ever to pass on undefined values. In other words, it will make the problem it solves even worse.
> if you find yourself needing deep accessors, something is very wrong with your scopes
I'm not sure I agree with that statement in all scenarios. For code you control, sure.
But there are many APIs that return very deeply nested structures that are inconsistent in their shape. That, in my view, is the most common place devs will need deep accessors where parts may be null/undefined somewhere in between the root object and the key they are trying to access.
Sure, they could write functions that, similar to get in lodash, expose just the values needed, at which point chaining wouldn't be needed at all in the code that deals with that value. Or it could be serialized into a class object, but again, the chain would be dealt with in the serialization. At some point, the chain needs to be dealt with and often the JSON structures from APIs are not something that's always under our control.
I agree that the main scenarios people are excited for are probably the exact opposite of the ones we should be looking to solve with this. Indeed my last comment right before posting this was about how callbacks are better than we remember (and if we were on ts when using them, probably wouldn't have minded at all).
Deeply nested object props are often bad, except for when it's a big dynamically-defined object (eg a directory tree that you would traverse with lodash get). It's also great when you're not deeply nested, you're simply checking for the inclusion of something in a collection that is itself optional. Eg a Map#has on an optional map.
It came to my mind because I've seen some Angular templates (they had this syntax before TypeScript had) where the dev just threw in some "?." to fix only the symptoms of a bug. This resulted in some ngIf condition to be always falsy and never showing a specific element.
The real bug was that the property in question should never be null/undefined in the first place. It was a lot harder to find the error.
Also, I've seen some typos (or missed renames) being unnoticed without any errors. This caused some weird behavior in the frontend where there was no exception but there is definitely something wrong. Or, worse, the bug is never noticed and other code depends on that behavior.
So if the language is statically typed, the feature is awesome because the cases mentioned above will trigger a compile time error.
I've seen some misuse in dynamically typed ones.
e.g. You can do:
Rather than: Agree that developers can be careful about nullability (in fact I pulled someone up on this in a code review earlier today), but I don't think this feature makes that any worse.