I think the necessity of strict typing correlates with fast prototyping vs scaled up core code.
For startups with quickly growing projects, TDD + a dynamic language is a very good way to stay fast and agile. But I've noticed that eventually you hit a point where you really want to rewrite core functionality in a stricter language to reach specific levels of safety which are extremely tedious to achieve with dynamic typing + TDD.
With Haskell, the type system is so good, that I've found that tests often don't even make sense — the type signature is pretty much guarding (and documenting) exactly what the function does.
Now that everything from python to PHP has typehints and JS has several typed dialects, I'd say: use them! You'll notice that they're going to save you in places where you thought tests were perfect.
There is a reason larger companies have come up with languages like Go, Typescript, Flow, Rust, Dart, HHVM, etc — they noticed dynamically typed code doesn't scale to the levels they needed.
I think optional typing introduced in Python 3.7 is a great addition. Like you, I now want to go back to some core elements and add stricter rules
There are ways of adding types to untyped Python code like google's pytype (static), monkeytype(dynamic) or some other library that randomly generates values into functions, runs thems, and sees what errors happen... However, at least monkeytype (its based on intercepting function calls and assuming that if you call a function with a list it must take a list object, but thats wrong because you can use any sequence). There was a talk where someone built one that randomly injected mock objects into code and given a type-inference algorithm would get the most general type (dynamically), I need to google that again, I tried building one myself too...
We're a place where coders share, stay up-to-date and grow their careers.
We strive for transparency and don't collect excess data.