Catch Python Bugs Before Runtime with Astral's ty Type Checker
Install Astral's blazing-fast ty, wire it into VS Code and pre-commit, and stop shipping type errors.
What you'll build
A Python project where type errors can't reach runtime: ty — the Rust-based type checker from Astral, the team behind uv and Ruff — checking your code from the CLI, flagging mistakes live in VS Code, and blocking bad commits with pre-commit.
Prerequisites
- Python 3.8 or newer (this tutorial was verified on Python 3.13.5, macOS).
- Versions verified: ty 0.0.64 (released 2026-07-27), ty-pre-commit v0.0.64, VS Code extension astral-sh.ty 2026.64.0.
gitandpip(or uv) on your PATH.- Note: ty is in beta, with 1.0 targeted for 2026. Diagnostics and defaults can shift between releases, so pin the version anywhere your team shares config.
1. Install ty
Pick whichever matches your setup:
uv tool install ty@latest # uv users
pip install ty # plain pip
For one-off runs without installing anything, uvx ty check also works. Confirm the install:
ty --version
ty 0.0.64 (5e64a131b 2026-07-27)
2. Create a project with a type bug
mkdir -p ty-demo/src && cd ty-demo
Create src/billing.py with two classic runtime landmines — a string passed where a number belongs, and a typo'd variable name:
def apply_discount(price: float, percent: int) -> float:
return price * (1 - percent / 100)
def format_total(total: float) -> str:
return f"${total:.2f}"
subtotal = apply_discount("100", 15)
print(format_total(subtotal))
print(format_total(missing_var))
3. Run your first check
ty check
error[invalid-argument-type]: Argument to function `apply_discount` is incorrect
--> src/billing.py:9:27
|
9 | subtotal = apply_discount("100", 15)
| ^^^^^ Expected `int | float`, found `Literal["100"]`
|
info: Function defined here
--> src/billing.py:1:5
|
1 | def apply_discount(price: float, percent: int) -> float:
| ^^^^^^^^^^^^^^ ------------ Parameter declared here
|
error[unresolved-reference]: Name `missing_var` used when not defined
--> src/billing.py:11:20
|
11 | print(format_total(missing_var))
| ^^^^^^^^^^^
|
Found 2 diagnostics
Both bugs caught without executing a line of code — and the check exits non-zero, which is what makes it CI-enforceable. Fix them by replacing src/billing.py with:
def apply_discount(price: float, percent: int) -> float:
return price * (1 - percent / 100)
def format_total(total: float) -> str:
return f"${total:.2f}"
subtotal = apply_discount(100.0, 15)
print(format_total(subtotal))
4. Configure ty in pyproject.toml
ty reads config from [tool.ty] in pyproject.toml (or a standalone ty.toml). Create pyproject.toml:
[project]
name = "ty-demo"
version = "0.1.0"
requires-python = ">=3.13"
[tool.ty.environment]
python-version = "3.13"
[tool.ty.rules]
possibly-unresolved-reference = "warn"
Each rule can be set to "error", "warn", or "ignore" — handy for adopting ty gradually on a loosely typed codebase.
5. Wire it into VS Code
Install the ty extension (ID astral-sh.ty). It bundles ty's language server, so you get the same diagnostics inline as you type, plus go-to-definition and completions. The extension automatically disables the Python extension's language server to avoid running two at once; override that with the python.languageServer and ty.disableLanguageServices settings if you want Pylance features alongside. PyCharm 2025.3+, Neovim, Zed, and Emacs setups are covered in the editors docs.
6. Add the pre-commit hook
pip install pre-commit
git init && git add -A
Create .pre-commit-config.yaml:
repos:
- repo: https://github.com/astral-sh/ty-pre-commit
rev: v0.0.64
hooks:
- id: ty
Then activate it:
pre-commit install
Unlike most hooks, this one checks your whole project rather than just staged files — deliberately, since a commit touching only a.py can surface new errors in b.py.
Verify it works
pre-commit run --all-files
ty.......................................................................Passed
And the direct check on your fixed code:
ty check
All checks passed!
To see the gate in action, reintroduce the "100" bug from step 2 and try git commit — the hook fails and the commit is blocked until ty check is clean.
Troubleshooting
error[unresolved-import]: Cannot resolve imported module requests`` — ty resolves third-party imports against a Python environment, and the package isn't in the one it picked (the info: lines under the error list every path it searched). Install the dependency into your project's .venv, or point ty at the right environment with ty check --python .venv. With an activated virtualenv or a uv-managed project, resolution is automatic.
ty: command not found right after installing — uv tool install places binaries in ~/.local/bin, which may not be on your PATH; run uv tool update-shell and open a new terminal. With plain pip inside a virtualenv, the binary lands in the venv's bin/ (Windows: Scripts\) directory, so the venv must be activated.
The ty hook fails on pre-commit.ci — the hook resolves your project's dependencies at run time, which requires network access that pre-commit.ci doesn't allow. Add ci: skip: [ty] to your config and run ty check as a regular step in your CI pipeline instead. Standard pre-commit in GitHub Actions works fine.
Next steps
- Browse the rules reference to tune severities per rule, and use
ty check --add-ignoreto bulk-insertty: ignoresuppression comments when onboarding a legacy codebase. - Try
ty check --fixto auto-apply available fixes. - Experiment with diagnostics in the ty playground before committing to config changes.
- Read the configuration docs for
ty.toml, per-user config, and precedence rules.
Sources & further reading
- Installing ty — docs.astral.sh
- Configuring ty — docs.astral.sh
- Editor integration — docs.astral.sh
- ty-pre-commit — github.com
- ty on PyPI — pypi.org
- ty extension for VS Code — marketplace.visualstudio.com
Lenn writes about cloud platforms, Kubernetes internals, and the infrastructure decisions that quietly make or break engineering organizations. Based in Berlin's vibrant tech scene, they have a talent for turning dense platform-engineering topics into prose that people actually finish reading.
Discussion 0
No comments yet
Be the first to weigh in.