State-of-the-art comparison of the five Python type checkers as of June 2026: mypy, basedpyright, ty, pyrefly, and Zuban. The Rust newcomers (ty, pyrefly, Zuban) have made mypy’s speed indefensible, but mypy’s plugin ecosystem and pyright/basedpyright’s spec conformance remain real moats. Conformance numbers move week-to-week, so re-verify against the live python/typing dashboard before citing. Related: Experience using UV (ty shares Astral’s uv/Ruff lineage), Parse, Don’t Validate (the value of pushing invariants into types).

Visual report: python-type-checkers-2026.html — open in a browser for the styled broadsheet version (gauges, deep-dive probes, gap tracker).

Legend for matrices below: ✅ full · ~ partial/buggy · ❌ none. Every cell is resolved against live conformance result files + issue trackers (no unknowns remain).

At a glance

mypybasedpyrighttypyreflyZuban
Vendorpython/ org (Lehtosalo et al.)DetachHead (solo)Astral (Ruff/uv)MetaDave Halter (Jedi author, solo)
LanguagePython (+ mypyc→C)TypeScript/NodeRust (Salsa)RustRust
Latest ver.2.1.0 (2026-05-11)1.39.8 / pyright 1.1.410 (2026-06-14)0.0.49 (2026-06-12)1.0.0 (2026-05-12)0.8.2 (2026-06-09)
MaturityMature referenceMature (inherits pyright)Beta (0.0.x)Stable (1.0)Beta (pre-1.0)
LicenseMITMITMITMITAGPL-3.0 + paid commercial
LineageThe original (~2012)Fork of MS pyrightNewRust rebuild of Pyre (OCaml)Independent; folds in Jedi

Conformance (official suite)

Live python/typing dashboard as of June 2026 (141 tests; dashboard versions: ty 0.0.48, pyrefly 1.0.0, zuban 0.7.2, pyright 1.1.409, mypy 2.1.0):

CheckerLive (Jun 2026)Mar 2026 same-day snapshot
Zuban99.3% (140/141)96.4%
basedpyright (= pyright)95.0% (134/141)97.8%
pyrefly92.2% (130/141)87.8%
ty70.9% (100/141)53.2%
mypy58.9% (83/141)58.3%

ty climbed ~18 points since March and now out-conforms mypy; Zuban has overtaken pyright at the top of the live dashboard (the March same-day run had it the other way). Caveat: conformance ≠ usefulness. Note the dashboard trails releases slightly: pyrefly’s entry is still 1.0.0 and ty’s is one patch behind (0.0.48 vs 0.0.49). Sources: Pyrefly conformance comparison (Mar 2026), live dashboard file tally (computed 2026-06-16).

Performance

All vendor numbers are upper bounds (hardware/parallelism cherry-picked). The three Rust checkers are roughly an order of magnitude faster than mypy/pyright on cold runs.

Cold full-checkIncremental/LSPParallel?
mypySlowest (baseline); home-assistant ~45sdmypy daemon (~10x repeat); no real LSPNew --num-workers (experimental, ~1.2–5x)
basedpyright~2–3x slower than mypyFast watch modeNo (single-threaded Node)
ty~2.2s home-assistant (~20x mypy); “10–60x” claimed~4.5ms PyTorch (best measured)Yes
pyrefly~1.85M LoC/s claimed; Instagram 20M lines ~30s<10ms rechecksYes
Zuban”20–200x faster than mypy” (vendor, no published harness)97ms first-completion (fastest in Posit’s test)Yes

Sources: Astral ty blog, Pyrefly speed/memory, pydevtools comparison.

Fine-grained capability matrix

Grounded in the live python/typing conformance results + each project’s issue tracker (June 2026). Columns: mypy · basedpyright · ty · pyrefly · Zuban.

A. Core type system

ty’s holes (variadics, generic aliases, protocols, variance-inference) live here, not in the headline conformance %.

FeaturemypybasedtypyreflyZuban
Return-type inference (unannotated)~~
TypeVar bounds / constraints / variance~
PEP 695 syntax
TypeVar defaults (696)~
Generic type aliases
ParamSpec + Concatenate (612)~~
↳ .args/.kwargs components~
TypeVarTuple + Unpack (646)
Self type (673)~~
Protocols: structural~
Protocols: @runtime_checkable
Protocols: recursive
Overload resolution
↳ overlap detection
↳ impl-consistency
↳ decorated overloads~
Literal (586) / LiteralString (675)
Final / ClassVar / Annotated
NewType / NamedTuple~
Enum (expansion / exhaustiveness)~

B. Narrowing & flow

ty’s TypeIs conformance test is currently regressing at 0.0.48 even though TypeGuard passes.

FeaturemypybasedtypyreflyZuban
Core (isinstance / None / truthiness / assert / walrus)
TypeGuard (647)
TypeIs (742)~
match-statement narrowing~~~
tuple-length narrowing
Reachability / unreachable code~
Exhaustiveness via assert_never~

C. TypedDict & dataclasses

The newest TypedDict PEPs (728 closed, 692 kwargs) show the widest spread. ty closed PEP 728 on 2026-06-12; mypy still tracks it open.

FeaturemypybasedtypyreflyZuban
TypedDict class + functional syntax
Required / NotRequired (655)
ReadOnly (705)
PEP 728 closed / extra_items
Unpack **kwargs TypedDict (692)~
dataclasses + dataclass_transform (681)
slots detection~
@override (698)~
@deprecated (702)~

D. Framework & library support

Mechanism is everything. mypy = runtime plugins (django/pydantic/sqlalchemy). pyrefly = native special-casing for pydantic/django. basedpyright/ty/Zuban have no plugin system — only dataclass_transform-based libs (modern pydantic, attrs, SQLAlchemy 2.0) and typeshed stubs work; anything needing a plugin (django-stubs ORM, classic attrs) degrades.

LibrarymypybasedtypyreflyZuban
pydantic v2✅ plugin✅ dc_transform~ stubs~ native~ dc_transform
Django ORM✅ plugin~ stubs~ native❌ planned
SQLAlchemy 2.0 (Mapped[])✅ native✅ dc_transform~ stubs~ stubs
attrs✅ built-in✅ dc_transform~ stubs~ dc_transform
pytest fixtures~❌ planned
FastAPI✅ via pydantic~~~
numpy / pandas (stubs)~~~~
functools.partial~~
General plugin system~ experimental❌ not planned

E. Adoption & ergonomics

Incremental adoption is the real-world deciding factor. basedpyright and pyrefly have first-class baselines/suppression; ty has neither yet.

FeaturemypybasedtypyreflyZuban
Baseline / ignore-existing-errors~ (basedmypy fork)✅ baseline.json✅ suppress~
strict preset✅ +all✅ mypy-compat
Per-rule severity config
Inline suppression w/ rule codes
Unused-ignore detection✅ on-by-default~ native-only
Migrate-from-mypy/pyright tooling✅ init✅ drop-in
First-class LSP~ rough
Parallel checking~ experimental

Unused-ignore nuance: ty ships unused-ignore-comment at default warn (on by default — more aggressive than mypy’s off-by-default --warn-unused-ignores). pyrefly has a native unused-ignore error kind (default-off) for # pyrefly: ignore only; the # type: ignore variant is still open (#3766, #3784). Zuban does not report unused # type: ignore yet.

Gap tracker (open issues behind the ❌/~)

ty (astral-sh/ty — longest list, beta): TypeVarTuple #156 (biggest hole) · generic aliases #1851 · overload overlap #103 · decorated overloads #2278 · tuple-length narrowing #560 / match #561 · Unpack kwargs #1746 · slots #3094 · pydantic #2403 / django #1018 / attrs #2404 · pytest #1986 · functools.partial #1536 · baseline #1074 · strict #527 · PEP 723 #691.

pyrefly (facebook/pyrefly — mostly framework depth): ParamSpec Concatenate name-loss #3706 · match-on-call narrowing #2858 · pydantic frozen #3663 / alias_generator #2942 · Django reverse rels #2071 / QuerySet #3648 · SQLAlchemy mapped_column #1600 · attrs field #1825 / private-attr #2053 · pytest fixtures #3776 · functools.partial #3175 · plugin/library API #22 · unused # type: ignore #3766 / #3784.

basedpyright (DetachHead/basedpyright + microsoft/pyright — architecture, not bugs): no plugin system by design, pyright #1782 / based #22 → blocks pydantic-deep #868 · pytest #311 · PEP 728 Unpack+extra_items edge #10352 · no global config #380. Django ORM / numpy depth are stub-bound, not engine bugs.

mypy (python/mypy — spec-lag, not architecture): PEP 728 closed TypedDict #18176 · Unpack kwargs #14697 · pytest fixtures (no plugin, manual typing) · no native baseline (only basedmypy fork) · plugin API powerful but explicitly experimental / no back-compat guarantee.

Zuban (zubanls/zuban — small project, gaps cited to docs/roadmap): unused # type: ignore not yet reported (docs) · no general plugin system (“not planned”) · Django plugin (roadmap: “next planned”) · pytest planned not shipped · SQLAlchemy / classic attrs need a plugin Zuban won’t load · functools.partial stub-driven (no plugin). Sub-features once marked unknown now verified ✅ via live conformance files (recursive protocols, overload overlap, tuple-length narrowing, assert_never exhaustiveness, PEP 728, slots all Pass at Zuban 0.7.2).

Feature deep-dive

How the same probe behaves across all five — where a shared ✅ hides opposite behaviour. (See Parse, Don’t Validate for why inference precision matters.)

i. Inference on unannotated code

Probe: x = [1, 2, 3] then x.append("foo").

Checkerinfers xverdict on append("foo")
mypylist[int]❌ error — but unannotated def bodies are skipped unless --check-untyped-defs
basedpyrightlist[int]❌ error — plus reportAny flags every Any usage site
tylist[Unknown]✅ accepted — the gradual guarantee; deliberately, can be unsound (#1248)
pyreflylist[int]❌ error — aggressive inference, finds bugs with zero annotations
Zubanlist[int]❌ error — zmypy mirrors mypy, zuban check mirrors pyright

ty alone accepts the bad append: its principle is that removing an annotation must never create an error, so the literal infers to list[Unknown]. Two caveats: no checker synthesises parameter types from call sites (unannotated params are gradual at the boundary in all five — the difference is locals/returns and whether the body is checked at all), and ty’s stance is a real soundness cost on container literals, not just conservatism.

ii. Type narrowing

Probes: match get_value(): (subject is a call), if len(t) == 2:, intersection/negation A & ~B.

Checkermatch on calltuple-lengthTypeGuard / TypeIsstandout
basedpyright✅ / ✅ two-sidedmost complete; reportUnreachable errors on dead branches
mypy✅ / ✅narrowing reworked in 1.20 (“more aggressive, consistent, correct”)
ty~❌ (#560)✅ / ~ regressingunique: intersection & negation narrowing (A & ~B)
pyrefly~ (#2858)✅ / ✅“flow types”; stricter — enforces protocol param-name matching
Zuban✅ / ✅mode-dependent (zmypy = mypy, check = pyright)

pyright/basedpyright is the completeness leader. Surprises: ty uniquely has intersection/negation narrowing yet lacks tuple-length, and its TypeIs is currently regressing while TypeGuard passes (the opposite of what you’d guess); pyrefly trips when the match subject is a call (bind it to a variable first).

iii. Overloads

Checkerresolutionoverlapconsistencyperformance
basedpyrightthe reference (Traut edits the spec); stricter than mypy — code passing mypy can fail here
mypyslow — resolution is combinatorial; overload-heavy code is mypy’s classic hotspot
ty❌ (#103)only matches mypy on overload-heavy SQLGlot, doesn’t beat it
pyreflyclean resolution; all overload conformance tests pass
Zubanzmypy reproduces mypy’s overlap errors in mypy’s format

pyright is the de-facto reference and strictest; ty’s one clear gap is overlap detection. Overloads are the rare place the Rust speed lead collapses — resolution is combinatorial for everyone, so ty only matches mypy on overload-dense code.

iv. pydantic & frameworks

Three-tier mechanism ladder: plugin (mypy) > native special-casing (pyrefly, pydantic + django only) > dataclass_transform + stubs (basedpyright / ty / Zuban).

CheckermechanismBaseModel initvalidators / computedSettings()Django ORM
mypyplugin
pyreflynative~~~ (ORM gaps #2071)
basedpyrightdc_transform~~ stubs
tydc_transform❌ (#1070)
Zubandc_transform~~ stubs

The bottom tier all get the constructor and field types right for free; what they cannot reach is runtime-semantic modelling: validator coercion (a validator taking str and storing int is invisible), a bare @computed_field loses recognition entirely, ORM query reshaping, and env-var population — so ty literally errors Settings() as missing a required api_key. This is mypy’s last, strongest moat.

IDE / LSP

  • mypy — no first-class LSP; batch CLI + dmypy, editors glue via pylsp-mypy / dmypy-ls (slow first run). Weakest interactive story.
  • basedpyright — full open-source LSP; re-implements Pylance-exclusive features (inlay hints, semantic tokens, docstring completion, import code-actions) that Microsoft locks to proprietary Pylance. Default in Zed.
  • ty — LSP-first architecture (Salsa); auto-import, inlay hints. Official VS Code ext, PyCharm/Neovim.
  • pyrefly — comprehensive LSP; default checker in PyCharm 2026.1.2, Antigravity, Cursor, Windsurf, Positron.
  • Zuban — LSP from the Jedi author, but editor maturity is the weak spot (manual setup, save-only analysis, a rename bug touching stdlib). Posit picked Pyrefly over it for Positron.

Bottom-line positioning

  • mypy — safe mature reference whose moat is now the plugin ecosystem alone: deep Django-ORM and pydantic-validator typing the Rust checkers can’t model. (SQLAlchemy 2.0’s Mapped[] is dataclass_transform-based and portable now — no longer mypy-only.) They match or beat it on everything else, and it stays slowest, LSP-less, and lowest-conformance (58.9%). Default for risk-averse, plugin-dependent teams.
  • basedpyright — pragmatic power-user pick: pyright’s top-tier engine + the only MIT path to Pylance-grade IDE features in any editor, plus baseline for incremental strict adoption. Single maintainer, single-threaded.
  • ty — fastest incremental LSP, cleanest Astral-ecosystem UX (uv/Ruff), now out-conforms mypy. Explicitly beta with “breaking changes between any two versions”; real type-system holes (TypeVarTuple, generic aliases, runtime/recursive protocols), no strict preset, no overload-overlap checks, no native pydantic/django — and by design the quietest checker (its gradual guarantee skips bugs in unannotated code that pyrefly flags). The one to watch.
  • pyrefly — most production-proven newcomer (Instagram 20M lines, PyCharm default), aggressive inference that finds bugs in unannotated code, native pydantic/django handling (ORM depth still thin), stable 1.0, Meta-backed.
  • Zuban — the most spec-complete of all five (99.3%, the conformance leader) and a true mypy drop-in (>95% of mypy’s test suite, reads existing config), fast. The blockers are non-technical: AGPL-3.0 (needs a paid license for many proprietary codebases), a solo-maintainer bus factor, no framework plugins, a still-rough LSP.

Key sources