There are far more compelling advantages to dynamic typing than fewer keystrokes.
Static type checking attempts to use compile time information to make claims about the runtime behavior of a program. This technique obviously fails to the extent that the runtime environment diverges from the compile time environment. Dynamic type checking, on the other hand, has no such limitation: it has perfect fidelity to the runtime environment because it occurs within that runtime environment. Even if the runtime environment itself changes at runtime!
Here's a concrete example. You compile a Haskell program that dynamically links a package. You then install a newer version of the package, which has an incompatible API change. Your program, which reported no type errors, now has a type error (a static typist may not agree this is a type error, but a dynamic typist would assert it is). And if your program starts at all, it will likely fall over and die, because GHC's codegen is brittle against such changes.
Fortunately, we do not suffer this fate on platforms like OS X or iOS, where binaries routinely run on multiple prior and future releases of the OS, with their accompanying shared libraries, while taking advantage of newer features whenever available. ObjC's dynamic typing is a big help here: it's simple to express "call this method, if it exists." And if you call a method that does not exist, you get a readable exception instead of being sent wildly through a random slot in a vtable.
(And oh yeah, having a stable ABI helps too.)
So this is one of the big strengths of dynamic typing: it works even if you don't know where exactly your program will run.
Sorry but claiming that executables written in a dynamically typed languages are resistant to invalid dynamic linking is not just bizarre, it's nonsense.
Either the code you are writing is 100% in the dynamically typed language and it couldn't care less about dynamic linking, or it's invoking functions in a dynamically linked library through an FFI, and it will fail like any other language.
What constitutes "invalid dynamic linking" is ABI and therefore language (and toolset) dependent. In C++, adding or removing a variable or virtual function from a class is an incompatible change: it will break binary compatibility with subclasses. In Haskell, adding a new entry to an algebraic data type also breaks binary compatibility. But in dynamically typed languages, these changes are usually benign.
The binary brittleness of C++ and Haskell is not a direct consequence of their lack of dynamic typing, but it is a consequence of the underlying assumption: that the compile time environment fully describes the runtime environment, and so no provisions need be made for their divergence.
I don't understand your second paragraph. Maybe you think that code written in dynamically typed languages cannot be compiled into shared objects? I work on a large Objective-C dynamic library, which is compiled to machine code and loaded by the dynamic linker, just like a C library. The dynamic aspects of ObjC pay us big dividends.
Static type checking attempts to use compile time information to make claims about the runtime behavior of a program. This technique obviously fails to the extent that the runtime environment diverges from the compile time environment. Dynamic type checking, on the other hand, has no such limitation: it has perfect fidelity to the runtime environment because it occurs within that runtime environment. Even if the runtime environment itself changes at runtime!
Here's a concrete example. You compile a Haskell program that dynamically links a package. You then install a newer version of the package, which has an incompatible API change. Your program, which reported no type errors, now has a type error (a static typist may not agree this is a type error, but a dynamic typist would assert it is). And if your program starts at all, it will likely fall over and die, because GHC's codegen is brittle against such changes.
Fortunately, we do not suffer this fate on platforms like OS X or iOS, where binaries routinely run on multiple prior and future releases of the OS, with their accompanying shared libraries, while taking advantage of newer features whenever available. ObjC's dynamic typing is a big help here: it's simple to express "call this method, if it exists." And if you call a method that does not exist, you get a readable exception instead of being sent wildly through a random slot in a vtable.
(And oh yeah, having a stable ABI helps too.)
So this is one of the big strengths of dynamic typing: it works even if you don't know where exactly your program will run.