I think that you combine when types are checked using how they are checked. Implementing runtime is not necessarily weak.
The main advantage of static types is exactly what you say: they are comprehensive. You can be sure that all call sites are of type by simply letting the compiler do this.
The main limitation of static types is that they are limited by the constraints that they can express. It depends on the language, with most languages ​​having relatively simple type systems (c, java) and others with extremely powerful type systems (haskell, cayenne).
Due to these limitations, types are not sufficient in themselves. For example, in java types there are more or less limited checks on type name matching. This means that the value of any constraint that you want to check must be encoded into some kind of naming scheme, therefore, a lot of pointers and boiler plates common to java code. C ++ is slightly better in that templates allow a bit more expressive, but do not come close to what you can do with dependent types. I'm not sure what the disadvantages of more powerful type systems are, although it is obvious that some people will use them in industry.
Even if you use static typing, most likely it is not expressive enough to check everything you care about, so you will also need to write tests. Regardless of what the static is gaining you more effort than the template requires, this is a discussion that has been raging for centuries and that I don't think it has a simple answer for all situations.
Regarding your second question:
How can we safely override the language at runtime?
The answer is tests. Your tests should cover all cases that matter. Tools can help you evaluate how comprehensive your tests are. Coverage verification tools let you know if lines of code are covered by tests or not. Test mutation tools (jester, heckle) can tell you if your tests are logically incomplete. Acceptance tests let you know what you wrote meets the requirements, and finally, regression and performance tests ensure that every new version of the product will maintain the quality of the latter.
One of the great things about proper in-place testing depends on complex types, which makes debugging a lot easier. When you run the tests, you get specific unsuccessful statements in the tests that clearly express what they are doing, and do not deceive statements about compiler errors (I think the errors of the C ++ template).
No matter what tools you use: writing code that you are sure of will take effort. Most likely, many tests will be required. If the penalty for errors is very high, for example, aerospace or medical control software, you may need to use formal mathematical methods to prove the behavior of your software, which makes such development extremely costly.