How to migrate from pyright to ty
ty, built by Astral, now part of OpenAI, uses different rules and configuration from Pyright. Keep Pyright running while comparing the same files, Python target, and dependency environment in ty.
Install ty alongside Pyright
In a project managed by uv:
uv add --dev ty
uv run ty check src/The --dev flag marks ty as a development dependencyA package needed during development (testing, linting, formatting) that is not shipped to users of your project. Installed with the --dev flag.
, keeping it out of production installs. uv run executes the command inside the project’s virtual environment, ensuring all dependencies are installed first.
A clean check prints All checks passed!; diagnostics include their ty rule names otherwise.
| Pyright command | ty command or limitation |
|---|---|
pyright src/ |
uv run ty check src/ |
pyright --pythonversion 3.12 src/ |
uv run ty check src/ --python-version 3.12 |
pyright --pythonpath path/to/python src/ |
uv run ty check src/ --python path/to/python |
pyright --warnings src/ |
ty already fails on warnings by default |
pyright --outputjson |
Use a supported ty report format; there is no matching Pyright JSON schema |
pyright --verifytypes pkg |
No equivalent API-completeness command |
Translate the project configuration
Pyright reads pyrightconfig.json or [tool.pyright] in pyproject.toml, with JSON taking precedence in the same directory. ty reads ty.toml or [tool.ty], with ty.toml taking precedence in the same directory. Both tools discover configuration by searching parent directories.
| Pyright setting | ty setting or migration step |
|---|---|
pythonVersion |
environment.python-version |
CLI --pythonpath or configured virtual environment |
environment.python accepts an interpreter or environment directory |
extraPaths |
environment.extra-paths |
include / exclude |
src.include / src.exclude; review glob behavior |
reportMissingImports |
rules.unresolved-import |
reportMissingTypeStubs |
No matching missing-stub diagnostic; do not disable all unresolved imports to imitate it |
reportArgumentType |
rules.invalid-argument-type for argument type mismatches |
reportReturnType |
rules.invalid-return-type |
reportAssignmentType |
rules.invalid-assignment for incompatible assignments |
reportOptionalSubscript |
rules.not-subscriptable for unsupported subscripting |
typeCheckingMode |
No matching basic or strict preset |
Rules are related checks, not exact diagnostic equivalences. Use Astral’s rule mapping when translating other report* settings.
For a src/ project targeting Python 3.12:
[tool.ty.environment]
python-version = "3.12"
[tool.ty.src]
include = ["src"]
exclude = ["tests/"]
[tool.ty.rules]
invalid-argument-type = "error"
invalid-return-type = "error"
invalid-assignment = "error"These three rule levels are explicit documentation of the project’s policy; they do not reproduce Pyright’s strict mode. In ty.toml, omit tool.ty from the table names. ty also merges user settings from ~/.config/ty/ty.toml on macOS/Linux or %APPDATA%\ty\ty.toml on Windows, so commit all settings CI relies on.
Compare inference and strictness
Both tools check unannotated function bodies, but neither has enough information to reject every operation involving an unannotated parameter. Pyright infers return types from those bodies; ty uses Unknown by default. To opt in to supported return inference in ty:
[tool.ty.analysis]
return-type-inference = "always"Review Pyright’s Unknown-type diagnostics and annotation requirements separately. Enabling all ty rules does not recreate them. Use ty’s individual rules and Ruff’s annotation rules where appropriate, following Astral’s strictness recommendations.
Pyright executionEnvironments bundle directory-specific import and environment settings. A ty override is useful for file-specific rule levels, but does not translate an entire execution environment:
[[tool.ty.overrides]]
include = ["src/legacy/**"]
rules = { invalid-assignment = "warn" }Warnings still fail ty by default. To make them advisory, set terminal.error-on-warning = false or pass --exit-zero-on-warning.
Resolve differences before suppressing them
Check the selected Python environment when imports fail. With uv, ty normally discovers .venv; --python .venv is an explicit fallback. Install missing runtime dependencies or stubs before deciding that an import error is expected.
Review nullable defaults, overridden parameter names, and framework-generated attributes against actual caller behavior. A larger or smaller diagnostic count alone does not show that either checker is more precise.
Use targeted suppressions where the project accepts an exception:
value: int = "legacy" # pyright: ignore[reportAssignmentType] # ty: ignore[invalid-assignment]The assignment is intentionally invalid migration debt. Prefer checker-specific comments to a bare # type: ignore, which suppresses multiple checkers. ty warns about unused ty-specific suppressions by default.
For a large backlog, generate ty-specific suppressions and review the source changes:
uv run ty check src/ --add-ignore
uv run ty check src/The second command should print All checks passed! once the remaining diagnostics are handled.
Replace the CI command after a parallel trial
After setting up uv in GitHub Actions, keep the existing Pyright job and add an advisory ty step:
- run: uv sync --locked
- run: uv run --locked pyright src/
- name: Evaluate ty
continue-on-error: true
run: uv run --locked ty check src/ --output-format githubThis assumes Pyright and ty are project development dependencies. Remove continue-on-error when the ty trial is clean, then remove Pyright’s step and dependency if its remaining checks are no longer needed. See the complete ty CI recipe.
ty exits 1 for warnings or errors by default, and 2 for fatal setup errors or invalid CLI usage. Pyright normally fails on errors and requires --warnings to fail on warning diagnostics.