The remaining problem with strong/static typing is that typically they will not enforce all important rules, or will enforce wrong ones.
Typing can be an incredibly powerful tool, but each language contains only a tiny subset, and part of this subset is often solving the wrong problem. Consider the following code:
void foo(not_null<int> pI, positive<double> d);
not_null is a type which, in constructor and assignment, tests whether parameter passed is null. Assignments between two not_nulls don't need to be checked, since invariants are preserved. The above is quite practical and efficient way to enforce invariants with zero overhead in almost all cases (in C and C++ at least).
While the above could be used as a powerful error detection mechanism, one that would fail reliably during run-time (excluding corrupt values), compiler does not understand it. Similarly, positive type could easily enforce constraint during run-time, but would still compile for positive(-1).
Ability to strongly type values can prevent a lot of errors - but current crop of compilers only provides a certain subset of these rules, and some of them are of dubious benefit (signed - unsigned conversion).
Type inference could help with previous examples, but is again just part of the equation. Some languages provide annotations, and those can be tested during build. Then there is AOP, which involves instrumentation and compile-time processing.
What it comes down to is actually two opposites: fail-fast vs. fail-safe.
In many languages, NullObject pattern became popular. It solves number 1 issue in managed languages (the annoying NPE) and helps simplify error checking. Similar idioms could be applied at language level as well.
Hungarian notation is just one attempt to address these deficiencies. Why isn't columnCount typed as Column (trivial and somewhat consistent fix, but explodes in complexity)? Could compiler be taught about what a column is? At this point, things get pretty interesting - compiler might deduce that Page is Margin + n*Column + n*Spacing.
Or given Url and EmailAddress, compiler would be able to deduce that EmailAddress + Url = Url (with login information). Or that Server + FilePath = URI. And that Server can be assigned to either IP4 and IP6 types. And that Username + Server = EmailAddress as well as LoginCredentials.
But it gets better. pInt + 0x443827465 might equal ThreadStack[1154] (due to the way memory is organized. Or, pInt[4] would equal localVariable7. And it would be perfectly correct and legit, but would make many language and best practices purists scream in horror. But all perfectly safe, enforced completely by compiler.
Today, this missing link is provided by unit tests. But those need to be hand-coded for each project. For some checks, especially in strongly typed languages, the type system gets in the way. NullObject cannot be reliably enforced in Java (lack of pass value by reference). In C++, there are so many corner cases that any such rich typing becomes incredibly complex and error-prone to implement.
But mostly, the software industry doesn't really care. Effectively all problems today are adequately solved by large numbers of warm bodies. So why improve the process in such drastic way, when the savings would not outweigh the training and educational effort.