I just use a combination of black, flake, and mypy. Apparently ruff is quite good as well. For lsp I use Jedi, which actually worked faster on my company's code base than pyright, a year or so ago.
So going forward my personal projects continue to be ducktyped, and if I care about types, I use rust or go.
I tend to run mypy every now and then ad-hoc to check things and it sometimes finds things that need correcting but I wouldn't say it's as worth setting up as the former.
an in my pyproject I set the strict mode for mypy, and almost all rules for rust with extend-select
just be aware a lot of community packages aren't ready for typed python. if you stick to the good stuff like fastapi and pydantic, you will be fine.
Having the tool get started with Ronacher, of Flask fame, gives it some mindshare among those paying attention.
It would be better still if the PSF were endorsing, but that gets too political, I suppose.
If it's new code, Pyright hands down.
The most recent one I've tried was Pyright. Pyright is faster than Mypy, and can also be configured to be quite strict, both big plus points for me. Unfortunately, in practice it ended up being far too strict in weird ways, and therefore difficult to work around with libraries that don't share its idiosyncratic assumptions.
For example, in Typescript there's an unknown type, which you can't do anything with unless you check its runtime type first. It's a clever way to add a true dynamic type to the type system without losing type safety. In Pyright there's a similar Unknown type, but in strict mode the type checker simply throws an error if anything is ever unknown. For me, this turned out to be particularly painful when using library types that involved generic state. Because I wasn't using that state, I didn't set a type for it, and it defaulted to Unknown. But that meant that an object had Unknown properties (that I was never using) which meant the type checker kept on failing. Moreover, because Python's generics story is pretty miserable, I couldn't figure out how to set the type to something not Unknown. And I couldn't figure out how to disable the error because Pyright's documentation is so poor. I genuinely spent a day of work on this problem, then disabled type checking completely because that was the easiest option.
I haven't used Mypy for over a year at this point, so things may well have changed, but my memory was that where Pyright is overly pedantic, Mypy was overly lax. It was also a lot slower, and had similarly poor documentation. The main reason I switched was that I was still regularly running into runtime type errors - the very errors that the type system was meant to help me avoid.
In both cases, I found the biggest problem was that neither tool seemed to go as far as Typescript in terms of modelling real-world Python code. What I mean by that is that idiomatic Typescript usually looks a lot like idiomatic Javascript in terms of the patterns and features used, because the Typescript team have put a lot of effort in to ensure that Typescript types represent how Javascript is actually used.
On the other hand idiomatic typed Python typically seems to look very different from idiomatic untyped Python, in large part because with types, you end up limited to about 1/4 of the surface area of the language. (This is why a lot of major libraries or tools come with type checker plugins because their library's behaviour cannot be described purely in terms of types.) Typed Python then ends up looking like a weird Java variant.
If I were going to add a type checker to my Python code again, I'd probably go for Mypy because it seems more like the standard and had marginally better documentation. But right now, I have the Pylance plugin installed in my IDE and that's about it - there's no type checking in CI, I just hope that I catch all the obvious cases with good enough testing. Thankfully the Python side of my work is pretty minimal, a few hundred lines of code at most. If I had a bigger Python project I might revisit this situation, but I'd be more tempted to just avoid Python for bigger projects.
But, Pycharm's still quite limited compared to pyright or even mypy. So I find myself A) doing an extra linting pass with pyright or mypy; and/or B) adding redundant annotations/overloads to help pycharm figure out what's going on.
My only issue is I occasionally need to restart Pyright when it fizzes out.
So mypy for me.
It serves my need, which is to ensure I haven't done mistakes in specifying parameters to functions.
I have no desire making Python to be statically-typed.
I enjoy typeguard for dynamic checking using the @typechecked decorator. I haven’t tried beartype, but I know lucidrains uses it.