Typing with tools like Pyright doesn't come close to providing what a good statically typechecked language provides.

There are many reasons for this. A big one is that many libraries are only partially typed at best, and dynamic types tend to propagate, weakening the guarantees you get from type checking.

Dynamic idioms in general, including something as common as string-indexed dictionaries, negate type checking. Runtime metaprogramming is the same. All of these things have equivalents in a good statically checked language, but Python doesn't follow those models.

Fundamentally, in Python static typing is an optional analysis layer over a dynamic language, and the consequences of that can't be fully mitigated. The result is a big difference in what types can guarantee.

TypeScript had this _exact_ same problem when it started out. As more libraries add annotations, the ecosystem will become stronger, and it will eventually be about as good as a "real" statically typed language.

> Dynamic idioms in general, including something as common as string-indexed dictionaries, negate type checking.

Do you have any proof of this? It hasn't been a problem in TypeScript, and I doubt it's an issue in Python