v5.0
live available
devtools guide 18 Oct 2026

Five Python type checkers against ten planted bugs, and the one that found none

Same file, same ten errors, default settings. Two found all ten, one found zero, and the zero is not a bug in the tool. It is the preset it ships with.

By Purvansh Parmar Checked 2026-10-21 Read 8 min Entries 5

Ten type errors in one small module, none of them the obvious kind. No integer added to a string. Nothing a reviewer catches in a second: a dataclass attribute that does not exist, a function that falls out of its only branch, a coroutine passed where the value was wanted, a dict returned from something annotated as returning a list. Then five checkers at their out of the box settings, because that is how almost everyone runs them. The spread was not what I expected: the quickest tool was also the most accurate, and the one that found nothing found nothing on purpose.

How this was checkedPlanted ten type errors in a 91 line inventory module, the kind that survive review rather than the kind you spot instantly, then stripped every marker from the file the checkers actually read. Ran five checkers at their defaults. ty and mypy found all ten with no false positives, in 89 and 240 milliseconds. pyrefly found zero, because its default preset is basic, and the same file under its strict preset produced ten errors.

What I compared on

01 ty

Astral's Rust type checker, still pre 1.0, run with no configuration file present.

Wins
It is the only tool here that is both the fastest and jointly the most accurate, and its diagnostics carry context blocks pointing at the declaration site, which is the part you need when the error is reported somewhere other than where you typed it.
Loses
It is version 0.0.90. The output format is not stable, those helpful context blocks are easy to miscount if you are parsing rather than reading, and nobody should be pinning a release process to it yet.
10/100 false positives89 ms

02 mypy

The reference implementation and the one most projects already have in their pre-commit config.

Wins
It finds everything at its defaults, explains itself better than anything else here, and is the tool that every error code, every stub package and every piece of advice on the internet is written against.
Loses
It is 2.7x slower than ty on one small file, and that gap grows on a real codebase where its caching behaviour and import following become the thing you spend configuration effort on.
10/100 false positives240 ms

03 pyright

Microsoft's checker, the engine behind Pylance and therefore the one most VS Code users are already running.

Wins
If you use VS Code you are already getting these nine findings as you type, with no pipeline step and no configuration, which is worth more in practice than a tenth finding in CI.
Loses
Slowest of teh three modern checkers by a factor of eight, it needs a Node runtime in whatever image runs it, and its basic mode is noticeably more permissive about optional values flowing into strict positions.
9/100 false positives1,938 ms

04 pytype

Google's inference based checker, which runs the code's types rather than only reading the annotations.

Wins
Its inference finds things in unannotated code that annotation readers cannot, which is the case it was built for and still the reason to reach for it on a legacy codebase with no type hints at all.
Loses
Ten seconds for one small file is 110x ty's time, and on a fully annotated module that inference bought nothing: it scored lowest of the four tools that found anything.
8/109,767 ms110x slower than ty

05 pyrefly

Meta's Rust checker, which selects a preset when it cannot find a configuration file and tells you which one.

Wins
It is honest about it. The line saying which preset it chose is printed on every run, so the zero is explained in the output rather than left to be discovered later by a type error in production.
Loses
A checker that reports success on a file containing ten type errors is going to be trusted by someone who did not read the second line of output. Shipping the permissive preset as the no-config default is the single most consequential decision in this comparison.
0/10 at default10 errors under strict260 ms

Side by side

FoundFalse positivesTime
ty 0.0.9010/10089 ms
mypy 2.4.010/100240 ms
pyright9/1001,938 ms
pytype 2024.10.118/1009,767 ms
pyrefly 1.4.0 (basic)0/100260 ms

Verdict

Run mypy in CI today and watch ty closely, because ty matched it exactly on this file at a third of the time and is the only thing here that might eventually make the choice interesting. The result to actually act on is pyrefly's. It reported a clean file containing ten type errors, not because it cannot find them, since its strict preset finds all ten, but because its no-config default does not look for them. Whatever checker you use, run it once against a file you have deliberately broken before you believe a green result from it, and check which preset it told you it had chosen.

Sources