How do mypy, pyright, and ty compare?
Python has three major static type checkers: mypy, pyright, and ty. All three analyze type annotations to catch bugs before runtime. They differ in implementation language, speed, strictness defaults, and how they handle untyped code.
Implementation and Speed
mypy is written in Python (with an optional compiled C extension via mypyc). It uses a multi-pass architecture that analyzes each module top-to-bottom, repeating until types converge. On large codebases, mypy can take tens of seconds or longer. Recent releases (1.18+) brought significant performance improvements, roughly 40% faster on mypy’s own codebase compared to earlier versions.
pyright is written in TypeScript and runs on Node.js. It uses lazy evaluation, computing types on demand rather than analyzing everything upfront. This makes pyright 3-5x faster than mypy on large codebases. Incremental mode and watch mode provide near-instant feedback during development.
ty runs 10-60x faster than both mypy and pyright without caching. Fine-grained incremental analysis makes editor feedback nearly instantaneous. Astral (the team behind Ruff and uv) built ty with the same performance-first philosophy as their other tools.
Strictness and Untyped Code
The three tools take fundamentally different positions on what happens when code lacks type annotations.
mypy skips unannotated functions by default. A function without annotations gets no type checking at all unless --check-untyped-defs or --strict is enabled. This surprises many users who expect mypy to catch obvious errors in functions they haven’t annotated yet. See how to configure mypy strict mode for details.
pyright checks all code by default, including unannotated functions, using inferred types. It provides five strictness levels (off, basic, standard, strict, all), with standard as the default. pyright infers types aggressively, which catches more bugs but can produce false positives in highly dynamic code.
ty follows a “gradual guarantee” approach. It checks all code but treats unknown types permissively, ensuring that adding type annotations to working code never introduces new errors. This makes ty practical to adopt in large, partially-typed codebases without a flood of false positives.
Configuration
Each tool stores configuration in different places with different formats.
mypy uses mypy.ini, .mypy.ini, setup.cfg, or pyproject.toml (under [tool.mypy]). Configuration supports per-module overrides, which is useful for treating vendored code or test files differently from application code.
pyright uses pyrightconfig.json or pyproject.toml (under [tool.pyright]). It supports execution environments for per-directory settings, letting monorepos configure type checking differently for each package.
ty uses ty.toml or pyproject.toml (under [tool.ty]). Individual diagnostic rules can be set to error, warn, or ignore, with per-file overrides available. User-level configuration lives at ~/.config/ty/ty.toml.
Editor Integration
pyright has the strongest IDE story. Microsoft built it to power the Pylance extension for VS Code, which provides autocompletion, hover information, go-to-definition, and rename refactoring. Pylance is closed-source, but pyright’s open-source language server can be used in other editors.
ty ships a full LSP implementation with go-to-definition, find references, completions with auto-import, rename refactoring, inlay hints, and signature help. Extensions are available for VS Code, Neovim, Zed, and PyCharm (2025.3+).
mypy does not include a language server. Editor integration relies on third-party plugins or IDE-specific implementations. Most developers who use mypy for CI checking pair it with pyright or ty in their editor.
Ecosystem and Maturity
mypy has been the default type checker since 2012. Most typing tutorials, blog posts, and Stack Overflow answers reference mypy. Libraries that ship type stubs (via typeshed or standalone packages) test primarily against mypy. Its error codes and suppression comments (# type: ignore[error-code]) are widely understood.
pyright has been in active development since 2019 with weekly releases. Microsoft maintains it with dedicated resources. It often implements new typing PEPs before other checkers, making it the most standards-forward option.
ty entered beta in 2025. It is the newest of the three and still filling out its support for the full Python typing specification. Some features that mypy and pyright handle may not yet be implemented. Astral tracks feature status in a GitHub issue.
Note
ty is in beta. Expect rapid progress, but also missing features and occasional breaking changes between releases.
Type Inference Differences
The tools sometimes disagree on how to infer types for the same code. One notable difference: empty containers.
x = [] # What type is x?
x.append(1)mypy infers list[int] by looking at how x is used later. pyright and ty infer list[Any], which is more permissive but less precise. These kinds of differences mean switching between type checkers may surface new errors or hide existing ones.
When to Choose Each
Choose mypy when:
- The project already uses mypy and the team knows its behavior
- CI pipelines and configuration are built around mypy
- Compatibility with the broadest set of typed libraries matters
Choose pyright when:
- VS Code is the primary editor and Pylance integration is valued
- The project benefits from aggressive type inference that catches more issues
- Staying current with the latest typing PEPs matters
Choose ty when:
- Speed is the primary concern, especially on large codebases
- The project is partially typed and false positives are a barrier to adoption
- The team already uses Ruff and uv and wants to stay within the Astral ecosystem
Running multiple checkers
Some teams run pyright or ty in their editor for fast feedback and mypy in CI for its broader library compatibility. This works because type annotations are the same across all three tools. Suppression comments differ (# type: ignore for mypy, # pyright: ignore for pyright, # ty: ignore for ty), so running multiple checkers in CI requires maintaining separate suppression comments.
Learn More
Get Python tooling updates
Subscribe to the newsletter