Skip to main content
Guides

ty vs mypy vs Pyright: Which Python Type Checker Should You Use in 2026?

By Mithun··9 min read
Quick Answer

Use mypy when you want a mature Python-first checker with broad project adoption and plugin support. Use Pyright when editor feedback and a highly granular strict configuration matter most. Try ty when fast command-line feedback is the priority and its newer ecosystem fits your project. In our pinned Python 3.14 fixture, all three caught the same nine typed-error categories by default; ty had the lowest fresh-process times. That small benchmark measures CLI startup plus three files, not large-project throughput.

Picking a Python type checker is no longer a two-way mypy-versus-Pyright decision. Astral's ty is now a credible third option, and speed claims make it tempting to choose from a benchmark headline alone. That misses the harder questions: do the checkers catch the same mistakes, what changes under strict settings, and which workflow does each one suit?

For this comparison, the same retained fixtures were checked with mypy 2.3.1, Pyright 1.1.413, and ty 0.0.72 on CPython 3.14.2. The test covers bad returns and arguments, None, generics, TypedDict, Protocol, overloads, an unresolved import, an untyped function, valid narrowing, and Python 3.14 template-string syntax. The versions, commands, outputs, timings, and limitations are recorded with the project evidence.

Test setup: same files, pinned versions

The main fixture contains nine intentional typed-error categories plus one valid narrowing function. An unresolved import and a valid Python 3.14 t"..." template string live in separate files, so an import or parser problem cannot prevent the core cases from being checked.

from typing import Protocol, TypedDict, overload
def bad_return(value: int) -> str:
return value # incompatible return
bad_return("wrong") # incompatible argument
maybe_name: str | None = None
maybe_name.upper() # None member access
class UserRow(TypedDict):
name: str
age: int
missing_age: UserRow = {"name": "Ada"}
class Renderer(Protocol):
def render(self, value: int) -> str: ...
A shortened sample of the retained fixture; every checker received the full same three files.
CheckerPinned versionDefault command style
mypy2.3.1 (compiled)mypy --python-version 3.14 ...
Pyright1.1.413 (npm)pyright --pythonversion 3.14 ...
ty0.0.72ty check --python-version 3.14 ...

For background and installation details, use the official mypy documentation, Pyright documentation, and ty documentation. The result below is our test, not a restatement of any vendor benchmark.

What each checker caught by default

On the deliberately typed cases, the result was a tie: mypy, Pyright, and ty each caught all nine error categories. None complained about the valid union-narrowing function, and all three accepted the separate Python 3.14 template-string file.

Test categorymypyPyrightty
Wrong return typeCaughtCaughtCaught
Wrong argument typeCaughtCaughtCaught
Access through str | NoneCaughtCaughtCaught
Wrong item in list[int]CaughtCaughtCaught
Missing TypedDict keyCaughtCaughtCaught
Wrong TypedDict valueCaughtCaughtCaught
Invalid Protocol implementationCaughtCaughtCaught
No matching overloadCaughtCaughtCaught
Unresolved importCaughtCaughtCaught
Valid union narrowingNo false positiveNo false positiveNo false positive
Valid Python 3.14 t-stringAcceptedAcceptedAccepted

Do not compare the raw totals as if a larger number means better checking. mypy and ty produced 9 diagnostics; Pyright produced 10 because it split the single overload mistake into a call error and an argument error. Comparing error categories is more meaningful.

Default versus strict checking

The unannotated function was the useful separator. None of the default runs reported it. mypy --strict raised the total from 9 to 11 by flagging the missing function annotation and the call into untyped code. Pyright strict raised its total from 10 to 17, adding granular unknown parameter, return, and member-type diagnostics.

ConfigurationDiagnosticsWhat changed
mypy default9The nine deliberately typed categories
mypy --strict11Also flags the untyped definition and call
Pyright default10Same nine categories; overload issue split in two
Pyright strict17Adds missing and unknown-type diagnostics
ty default9The nine deliberately typed categories

ty is intentionally not shown with a fake “strict” row. Its current configuration model exposes individual rules and severities rather than one preset directly equivalent to mypy --strict or Pyright typeCheckingMode: "strict". The official mypy/Pyright migration guide for ty explains those differences.

Strict modes exposed the risk of untyped code, but they did not magically infer the runtime failure inside a parameter typed as unknown or Any. Strictness improves annotation coverage; it is not a replacement for tests.

Speed result: ty won this small CLI test

Each default command was started as a new process 11 times against the same three small files on Windows, using an AMD Ryzen 5 3500. The first timed process is separate; the repeated value is the median of the next ten fresh-process runs.

CheckerFirst timed processRepeated medianRepeated range
mypy 2.3.11268.6 ms259.3 ms254.6–274.7 ms
Pyright 1.1.413594.9 ms569.6 ms555.4–571.6 ms
ty 0.0.72165.5 ms55.8 ms53.7–60.1 ms

ty was fastest in this setup: its 55.8 ms repeated median was about 4.6× lower than mypy's and 10.2× lower than Pyright's. That is useful for quick CLI feedback, but it is not evidence that the same ratio holds for your repository. This fixture is tiny, startup dominates, earlier diagnostic runs had already warmed OS caches, and no daemon, editor, or incremental workflow was benchmarked.

mypy: the conservative Python-first choice

mypy is the easiest recommendation when a project already has mypy configuration, plugins, suppressions, and CI history. Its error codes were compact and familiar in this run, and --strict made the move from typed islands to annotation coverage obvious. It is also the checker used by our online Python Type Checker, so a small snippet can be checked without local setup.

Choose mypy when: compatibility with an established Python typing workflow matters more than winning a startup benchmark; your framework depends on a mypy plugin; or the team already understands mypy error codes and configuration.

Pyright: granular strictness and editor-oriented feedback

Pyright produced the most detailed strict output here. That can feel noisy in a migration, but it gives a team precise control over unknown types and incomplete annotations. Its command-line package is installed through npm, and its configuration supports named checking modes plus individual diagnostic rules.

Choose Pyright when: fast editor feedback is central to the workflow, the team wants granular rule-by-rule enforcement, or strict unknown-type reporting is more valuable than the smallest terminal output. If switching from mypy, test the real codebase first—the official comparison documents behavioral differences.

ty: fastest here, with a newer compatibility surface

ty had the lowest startup-inclusive times and still caught every deliberately typed category in our fixture. Its concise messages were readable, and installation can be as simple as pip install ty. The trade-off is not diagnostic ability in this small test; it is project risk. A newer checker may differ on advanced typing behavior, configuration, editor workflows, plugins, and third-party assumptions built around mypy or Pyright.

Choose ty when: very short feedback loops matter, you can validate it against your own repository, and you are comfortable checking compatibility before replacing an established gate. A practical adoption path is to run ty alongside the current checker first, compare disagreements, then decide whether it can become the gate.

Which Python type checker should you use?

Your priorityBest starting pointWhy
Existing Python project with mypy history or pluginsmypyLowest migration cost and mature project conventions
Granular strict rules and editor-first workflowPyrightDetailed unknown-type diagnostics and configurable modes
Fast local CLI feedbacktyLowest fresh-process times in this controlled test
Learning or checking one snippet in a browsermypy onlineUse the site tool without local installation
Large production repositoryBenchmark two on your codeOur three-file startup test cannot predict repository behavior
  1. Run the candidate checker beside your existing gate; do not replace CI from a generic benchmark.
  2. Compare disagreements in your hardest modules: framework code, generated code, stubs, overloads, and untyped boundaries.
  3. Choose a strictness baseline deliberately, then reduce exceptions over time instead of enabling every rule blindly.
  4. Measure both clean runs and the editor or incremental loop developers actually feel.

For most existing codebases, the best checker is the one that catches useful bugs without causing the team to ignore the gate. Migration cost, false positives, plugins, and editor behavior can outweigh a synthetic speed win.

Reproduce the comparison—and read the limits

mypy --python-version 3.14 --show-error-codes core_cases.py missing_import.py python314_syntax.py
mypy --python-version 3.14 --strict --show-error-codes core_cases.py missing_import.py python314_syntax.py
pyright --pythonversion 3.14 core_cases.py missing_import.py python314_syntax.py
pyright --project pyrightconfig.strict.json
ty check --python-version 3.14 core_cases.py missing_import.py python314_syntax.py
Default and strict commands used for the retained fixtures.

The evidence records exact versions, configuration, diagnostic outputs, all 11 timing samples, and the test machine. The important limits are equally explicit: one Windows machine, three tiny files, startup-heavy fresh processes, warmed OS caches, and no large-project, daemon, incremental, editor, plugin, or third-party-stub benchmark.

Before choosing, replace our fixture with your own modules. If you only need to inspect a function or reproduce a mypy error quickly, open the Python Type Checker. For style and correctness rules outside static typing, use the Ruff Playground as a separate linting step.

Check your annotations now

Paste a typed Python function, run mypy in the browser, and switch on strict mode when you want annotation-coverage checks. No local setup is required.

Open the Python Type Checker

Free tools mentioned here

Related guides

Frequently asked questions

Is ty faster than mypy and Pyright?

It was fastest in our small Windows fresh-process test. ty 0.0.72 had a 55.8 ms repeated median, versus 259.3 ms for mypy 2.3.1 and 569.6 ms for Pyright 1.1.413. This measured CLI startup plus three tiny files, not large-project or incremental performance, so benchmark your own repository before deciding.

Does ty catch the same errors as mypy and Pyright?

For our nine deliberately typed categories, yes: all three caught incompatible returns and arguments, None member access, a generic mismatch, two TypedDict errors, a Protocol mismatch, an overload mismatch, and an unresolved import. That representative fixture does not prove identical behavior for every typing feature or third-party package.

Is Pyright stricter than mypy?

Their strict presets emphasize and count diagnostics differently. In our fixture, mypy strict produced 11 diagnostics and Pyright strict produced 17 because Pyright added several granular unknown parameter, return, and member-type messages. A higher raw count does not automatically mean better checking; compare the actual categories and usefulness on your code.

Does ty have a strict mode like mypy or Pyright?

Not as one directly equivalent preset in the version tested. ty exposes individual diagnostic rules and severities, while mypy provides --strict and Pyright provides typeCheckingMode: strict. Configure the rules you need rather than calling a ty run strict without defining it.

Which Python type checker is best for beginners?

mypy is a practical starting point because its messages, documentation, and examples are widely used, and you can try it in our browser-based checker. Pyright is attractive for editor-first feedback. ty is easy to install and was very fast here, but a beginner should still learn what the reported types mean instead of choosing only by speed.

Keep reading