ty: The Blazingly Fast Python Type Checker from Astral (Ruff & uv Creators)
Source: Dev.to
Key Features
- ⚡ Blazingly Fast – 10–100× faster than mypy and Pyright
- 🔍 Rich Diagnostics – Detailed error messages with multi‑file context
- 🧠 Smart Type Inference – Catches bugs even without type annotations
- 🎯 Advanced Type System – Intersection types, advanced narrowing, reachability analysis
- 🛠️ Language Server – Built‑in support for IDE integration (VS Code extension available)
Installation
# Using uv (recommended)
uv tool install ty@latest
# Using pip
pip install ty
Basic Usage
# Check a file
ty check file.py
# Check directories
ty check src tests
# Verbose mode
ty check src --verbose
What Makes ty Special?
1. Speed That Matters
ty can type‑check the entire Home Assistant project in ~2.19 seconds, compared to mypy’s 45.66 seconds – over 20× faster!
For large codebases, this speed difference transforms the developer experience:
- No more waiting for CI to finish
- Instant feedback in your IDE
- Practical to run on every save
2. Powerful Type Inference
ty can find bugs even without type annotations. Below are some real‑world examples.
Real‑World Bug Detection
Example 1: Type Mismatches
def add(x: int, y: int) -> int:
return x + y
result: str = add(1, 2) # Oops! Assigning int to str
ty’s output
error[invalid-assignment]: Object of type `int` is not assignable to `str`
--> example.py:4:9
|
4 | result: str = add(1, 2)
| --- ^^^^^^^^^ Incompatible value of type `int`
| |
| Declared type
Example 2: Missing Attributes
class User:
def __init__(self, name: str):
self.name = name
def get_email(self):
return self.email # email was never defined!
ty catches this
error[unresolved-attribute]: Object of type `Self@get_email` has no attribute `email`
--> example.py:6:16
|
6 | return self.email
| ^^^^^^^^^^
Even for more complex cases:
class DatabaseClient:
def __init__(self, connection_string: str):
self.connection = connect(connection_string)
def query(self, sql: str):
# Typo: should be .execute(), not .exec()
return self.connection.exec(sql)
ty will warn you that .exec() doesn’t exist on the connection object.
Example 3: Incorrect Function Arguments
numbers: list[int] = [1, 2, 3]
numbers.append("four") # String in int list!
ty’s error
error[invalid-argument-type]: Argument to bound method `append` is incorrect
--> example.py:2:16
|
2 | numbers.append("four")
| ^^^^^^ Expected `int`, found `Literal["four"]`
Example 4: Null Safety
def process(value: str | None) -> int:
return len(value) # What if value is None?
ty spots the issue
error[invalid-argument-type]: Expected `Sized`, found `str | None`
--> example.py:2:16
|
2 | return len(value)
| ^^^^^
|
info: Element `None` of this union is not assignable to `Sized`
Example 5: Implicit None Returns
def get_name(user_id: int) -> str:
if user_id > 0:
return "User"
# Missing return statement – implicit None!
ty catches this too
error[invalid-return-type]: Function can implicitly return `None`,
which is not assignable to return type `str`
Configuration
ty uses pyproject.toml for configuration:
[tool.ty]
[tool.ty.environment]
python-version = "3.10"
[tool.ty.src]
include = ["src/**/*.py", "tests/**/*.py"]
exclude = [".tox/**", "build/**", "dist/**"]
# Per‑directory overrides
[[tool.ty.overrides]]
include = ["tests/**/*.py"]
[tool.ty.overrides.rules]
invalid-assignment = "ignore" # More lenient for tests
CI/CD Integration
GitHub Actions
name: Type Check
on: [push, pull_request]
jobs:
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install dependencies
run: pip install ty
- name: Run type checker
run: ty check src tests
tox Integration
[testenv:ty]
deps =
ty
-e .
commands =
ty check src tests
pre‑commit Hook
repos:
- repo: local
hooks:
- id: ty
name: ty type checker
entry: ty check src tests
language: system
pass_filenames: false
types: [python]
Enjoy the speed and safety that ty brings to your Python projects!
Editor Integration
VS Code
- Install the “ty” extension from the VS Code Marketplace.
Enjoy automatic:
- Type checking as you type
- Inlay hints for inferred types
- Code actions and quick fixes
- Jump‑to‑definition with type awareness
ty vs mypy vs Pyright
| Feature | ty | mypy | Pyright |
|---|---|---|---|
| Speed | ⚡⚡⚡ Ultra‑fast | 🐢 Moderate | 🚀 Fast |
| Language | Rust | Python | TypeScript |
| Error Messages | 📝 Very detailed | 📄 Standard | 📝 Detailed |
| Type Inference | 🧠 Powerful | 📚 Standard | 🧠 Powerful |
| Maturity | 🆕 Beta | ✅ Stable | ✅ Stable |
| Diagnostics | 🔍 Multi‑file context | 📊 Single‑file | 🔍 Cross‑file |
| Language Server | ✅ Built‑in | ⚠️ Via dmypy | ✅ Built‑in |
Current Limitations
As of v0.0.9 (Beta):
- Limited rule set – Main rules implemented; more coming in 2026.
- No strict mode yet – Won’t complain about missing type annotations.
- Beta status – API may change before the stable release.
Even in beta, ty already catches real bugs and provides value in production codebases.
Getting Started with ty
Step 1: Install and Run
uv tool install ty@latest
ty check src
Step 2: Review Results
ty will show you actual bugs in your code, even without type annotations!
Step 3: Add to CI
# Add to your test script
tox -e ty
# Or run directly
ty check src tests
Step 4: Gradual Type Annotation
# Before
def calculate_total(items):
return sum(item.price for item in items)
# After
def calculate_total(items: list[Item]) -> Decimal:
return sum(item.price for item in items)
With annotations, ty becomes even more powerful!
Real‑World Example: FastAPI Application
from fastapi import FastAPI, HTTPException
app = FastAPI()
class UserService:
def __init__(self, db_url: str):
self.db = connect_database(db_url)
def get_user(self, user_id: int):
user = self.db.query(User).filter(User.id == user_id).first()
if user:
return user
# Bug: FastAPI expects HTTPException, but we're returning None!
return None
@app.get("/users/{user_id}")
async def get_user(user_id: int):
service = UserService(DB_URL)
user = service.get_user(user_id)
# Bug: user could be None, but we're accessing .dict()
return user.dict()
ty will catch both bugs:
get_usercan returnNone, but FastAPI expects an exception or a valid user.user.dict()could fail ifuserisNone.
Performance Tips
- Use file exclusion – Skip generated files and vendor directories.
- Enable caching –
tycaches results for unchanged files. - Parallel processing –
tyautomatically uses multiple cores. - Incremental mode – In your editor, only changed files are re‑checked.
Roadmap
According to Astral, ty targets a stable 1.0 release in 2026, with plans for:
- ✅ More comprehensive rule coverage
- ✅ Strict mode (enforce type annotations)
- ✅ Better integration with popular frameworks
- ✅ Plugin system for custom rules
- ✅ Performance optimizations
Conclusion
ty represents the next evolution in Python type checking. Its combination of blazing speed, powerful inference, and excellent diagnostics makes it a compelling choice for Python developers.
While still in beta, ty is already proving valuable for catching real bugs and improving code quality. If you’re using mypy or Pyright and are frustrated by slow check times, ty is worth trying.
The Astral team has an excellent track record with Ruff and uv, and ty looks set to be another game‑changer in the Python tooling ecosystem.
Resources
- 🌐 Official Website:
- 📚 Documentation:
- 💻 GitHub:
- 💬 Discord:
Have you tried ty yet? What’s your experience with Python type checkers? Let me know in the comments! 👇