The point of errors in programming is that they inform you of a serious contract violation or illegal state. You expected something to be defined, but it wasn't. You expected something to be a certain type but it wasn't. Usually, the program shouldn't try to continue under a contract violation, and if it should, you'd want to make that explicit and at least log the violation rather than suppress it completely with head buried in the sand.
In many languages, these sort of invariant violations and illegal operations cause explicit errors, either at runtime or in compilation. The earlier you can catch the problem (thanks to a compiler error, warning or runtime crash/error during testing), the sooner and easier you can fix the bug.
JS has a reputation as stubborn to raise errors due to weak typing and other permissive design choices. You can concatenate and perform math on pretty much any types, or compare values with coercive equality operator ==
and wind up with anyone's-guess results. The reason is mostly historical: JS was designed for quick, simple scripts, not for massive single-page applications of today.
The reason technology like TypeScript exists is to essentially add errors to JavaScript to make programs safer and support the sort of large, industrial-grade applications that were never envisioned in 1995 when JS was designed. These errors are purely compile-time in the case of TS, but other languages like Python won't let you do many of the things JS does at runtime, like adding two different typed values "abc" + 42
which raises a TypeError
(dictionary [a sort of Map
] accesses also raise an error; you have to opt-in to the default value equivalent of undefined
).
Simarly, JS' strict mode "eliminates some JavaScript silent errors by changing them to throw errors".
The contrary philosophy is to suppress errors, which is exactly what the optional chaining operator ?.
offers, and it sounds like you're suggesting the default object property operator .
should as well. This is syntactical sugar for code that returns undefined
when an object doesn't exist, and it's nice to use occasionally to keep code terse and communicate intent, saying essentially "look, I realize this particular access might be on an undefined object, and my code is designed such that it expects that case and will continue on with the expression evaluating to undefined
".
But it's a good thing this isn't the default behavior: it's essentially suppressing an error, pretty much the same rationale behind "abc" * 42
returning the default value NaN
instead of throwing a type error in JS. Why would you ever want this? Probably never, hence JS's reputation as unsafe.
As I mentioned in the comments, React also made changes to ascribe to the "fail fast, fail loudly, fail hard" philosophy:
[...] in our experience it is worse to leave corrupted UI in place than to completely remove it. For example, in a product like Messenger leaving the broken UI visible could lead to somebody sending a message to the wrong person. Similarly, it is worse for a payments app to display a wrong amount than to render nothing.
[...] This change means that as you migrate to React 16, you will likely uncover existing crashes in your application that have been unnoticed before.
[...] We also encourage you to use JS error reporting services (or build your own) so that you can learn about unhandled exceptions as they happen in production, and fix them.
Here's advice from an article on javascript.info:
Don't overuse the optional chaining
We should use ?.
only where it's ok that something doesn't exist.
For example, if according to our coding logic user
object must exist, but address
is optional, then we should write user.address?.street
, but not user?.address?.street
.
So, if user
happens to be undefined due to a mistake, we’ll see a programming error about it and fix it. Otherwise, coding errors can be silenced where not appropriate, and become more difficult to debug.
And here's a nice blog post on the matter