The way I look at it is, if you can work naturally within a statically typed language, then static typing is the way to go. In general, the purpose of a type system is to prevent you from performing operations with undefined semantics - like (string) "hello" + (bool) true
. Having the extra level of safety preventing you from performing these operations can be a good way to prevent bugs in your code, even without extensive unit tests. That is, type-safety provides another level of confidence in the semantic correctness of your code.
But type systems are very hard to get right. I don't believe there is a perfect type system in nature at the time of this writing. (By "perfect type system", I mean a strict type system, that doesn't require verbose code annotations, that generates no false-positive type errors, and whose type errors are easy for the programmer to understand.) Further, it can be difficult to understand the really good type systems that do exist. When I was learning Haskell, I can't tell you the number of obscure type errors that I got while attempting to write what looked (to me) like correct code. Usually the code wasn't actually correct (which is a point in favor of the type system), but it took a lot of work to understand the error messages from the compiler, so that I could correct the underlying problems. In OO languages, you may eventually find yourself thinking "this argument should be contravariant with the input type, not covariant!", or (more likely) reverting to typecasts to escape from the bounds of the type system. Type systems can get much trickier than you'd think.
For what it's worth, it's my understanding that the difficulty in coming up with good type systems is part of what motivated Gilad Bracha to include pluggable type-system support in Newspeak.