> The silent data corruption is really only a problem with weak dynamic typing, that usually automatically coerces types.

That's not necessarily true. A function could serialize the passed value, which would work without type conversion, and it could still result in data corruption somewhere down the line. The point is that with dynamic typing there's no guarantee of correctness. It has nothing to do with strong vs. weak typing, which incidentally I don't find helpful debating, since there's no single definition for those terms, and most languages can behave arbitrarily depending on the situation.

Furthermore, you ignored my primary point of runtime type errors. These are very common in Python, and there's really no solution to them besides doing offline type checking, which as I said, has its own problems and is not a silver bullet either.

> And usually in practice you end up testing all of the code you're writing anyone, so this almost never happens in practice except when someone is refactoring without thoroughly testing what they did, which should be done anyway whether there are types or not.

Assuming you were referring to data corruption, maybe. But type errors happen very often in practice, and no amount of testing can guarantee you won't run into them. Besides, most teams I've worked with weren't disciplined enough to achieve even 100% statement coverage, let alone branch coverage, or do more sophisticated testing like fuzzing. So while type errors are close to impossible to prevent by testing, even data corruption can easily fly under the radar.

Static typing gives you this safety net, _for free_. This alone is worth the minor inconvenience of having to specify type information, and think about types explicitly.

> They probably do bother when it's an important module, or a an edge boundary that needs to be documented with a contract, or during times of significant refactoring.

This requires experience to know good practices, when to follow them, and the discipline to do so. IME very few developers are this diligent 100% of the time, and most, if given the option, will do the minimum amount of work necessary. I'm not just blaming others, I've been lazy about good practices myself many times. This is why gradual or optional typing is not a solution to these issues.

Looking at it from the other direction, most statically typed languages can do type inference. This avoids the tedium of having to be explicit all the time, while still giving you the benefits of type checking at compile time. This is a much safer solution.

> And these days LLMs can generate the specs, optional types, and tests pretty easily for any sort of self-contained, modular, reasonably well written code.

Seriously? LLMs have no place in a discussion about correctness. They're glorified autocomplete engines, which can be useful, but trusting them to give you correct output for these issues is incredibly risky. At best you would need to manually verify everything they do, and I trust myself to do a quicker job in most situations with macros and `sed`.

> And type completion in an IDE still exists for a bunch of dynamically typed languages anyway, like javascript.

I feel like we're talking about two different things, and you're ignoring the main issue of type errors at runtime.