I've been writing JS almost two decades at this point, but I still can't see why ?.
is not the beginning of an anti-pattern, especially for method calls.
I saw the post
20 JavaScript Tricks Every Developer Must Know 🚀
Jagroop Singh ・ Oct 28
And one that caught my eye was number 2 "Optional Chaining with Function Calls":
const user = {
getName: () => 'Alice',
};
console.log(user.getName?.()); // Alice
console.log(user.getAge?.()); // undefined
Which screams "I don't know the interface to my objects, and I don't care."
You definitely should care about the interface to your objects.
I can almost see an argument for robustness if a library API changes, but no, if you're using a library, you know the interface to the objects, fix your code.
It just seems like a massive bug attractor.
You never want something to be undefined
. Or have I missed a memo about that?
And that goes double for methods. If two objects don't have the same behaviours, they need to be treated as different types, not just shrugging and saying "it'll be alright".
Is testing for null
illegal these days? I know null
has been called a billion dollar mistake, but that is solved by an option type (like Maybe
in Haskell), not what looks like the inverse.
An option type lets you know that this is something that could be null, so proceed with caution. It doesn't hide the fact, like ?.
does.
With all that said, I'm happy to be disabused of the notion that ?.
is just kicking the can down the road when it comes to checking for null
, or is just bad design when it comes to undefined
.
What is a good pattern, i.e. one that reduces bugs, that you can get using ?.
?
Top comments (2)
Optional chaining is still testing for null(ish). Just a different way to do it.
I find optional chaining most useful when you do not have a safe/reliable data/object contract. There are lots of reasons this can be the case.
If you have an API where success cases have a
status
object and error cases have acause
object, you might writeresponse.status?.code ?? response.cause?.code
to simplify your handling.Versioned objects are a major consideration for optional chaining. If you add an audit log to your transaction record at some point, you can check for
transaction.audit != null
but you could save yourself an extra layer withtransaction.audit?.length
which will tell you not only that the audit log exists, but that there are entries in it to display. It effectively replacesif (transaction.audit && transaction.audit.length)
. Sure, you can implement this a dozen different ways, but optional chaining can be a pretty reasonable one.I generally dislike OOP, so with imperfect abstractions my mind doesn't go to extended classes, but to positioned content on the DOM. If you have a custom dropdown the list might have to be applied to the
body
and positioned to ensure it isn't cut off by a container. As a result, you may or may not have related elements or methods depending on where in the DOM your content is injected. You can choose between a bunch of guards, or you can use optional chaining to achieve the same result with less noise.Does this mean one API URL (e.g. example.com/getARecord) can return different objects independent of your input? I'll admit that's a good case for
?.
. I would also council moving away from such a haphazardly implemented API where possible.Versioned data contracts mean having different calls for each version. So long as the contracts are valid you have different methods for interacting with them.
I will admit
if(transaction.audit?.length)
is pretty neat. I like that. Data structures that can have varying properties due to old data is a use case I can understand.Thanks for your reply. It helped me understand what
?.
is good for.