--- foo = bar[key] or null +++ foo = if bar[key]? then bar[key] else null
The problem arises if bar[key] is a defined, useful value that happens to also be a false value — like an integer 0, an empty string, or (to state the obvious), the boolean false. In that case, since the first expression is a false value, the whole expression evaluates to the second expression, null; the original line translated a 0 into a null.
Here's my issue with that: it's always valid code, and sometimes it's a valid pattern, but the language doesn't tell you when. And that, in a nutshell, is my beef with weak typing.
Now, there are lots of ways to write bugs in code, and it's reasonable to ask why I'm singling out this one class of bugs. I would argue that whereas other bugs are due to misrepresenting a problem, or to applying the wrong solution to a problem, this bug is due to applying the wrong dialect of the correct solution. Ugh. Debugging is enough of a pain without the compiler encouraging us to write the programming equivalent of a tongue-twister. Weak typing makes it easy to write bugs, and, once you've written them, the type system does absolutely nothing to help you find them.
When it comes to programming, I want the computer to mean what I say. The quid pro quo is that I need to say what I mean. If what I mean is "bar[key] if it exists, otherwise null," then the language should require that I just say that. It's well and good for a language to make that pattern succinct, but blurring the lines between boolean logic and plain-old-values is funky reasoning that can lead to funky bugs.