12 KiB
12 KiB
ty - Extremely Fast Python Type Checker
Official Docs: https://docs.astral.sh/ty
What is ty?
ty is an extremely fast Python type checker written in Rust by Astral (the creators of uv and Ruff). It's designed to be:
- 10-100x faster than mypy and pyright
- Zero configuration to get started
- Compatible with existing type annotations
- Production-ready for large codebases
Think: "Ruff for type checking" - blazing fast, modern, and built for scale.
Installation
Add to your project dependencies:
# With uv (recommended)
uv add --dev ty
# With pip
pip install ty
Or run it directly without installing:
uvx ty check
Quick Start
1. Basic Usage
# Check entire project
ty check
# Check specific files/directories
ty check src/
ty check src/models/User.py
# Watch mode (recheck on file changes)
ty check --watch
2. Exit Codes
0- No errors1- Type errors found2- Invalid config/CLI options101- Internal error
3. Output Formats
# Default verbose output with context
ty check
# Concise (one per line)
ty check --output-format concise
# GitHub Actions annotations
ty check --output-format github
# GitLab Code Quality JSON
ty check --output-format gitlab
Configuration
Option 1: pyproject.toml (Recommended)
[tool.ty]
# Python version (auto-detected from requires-python if not set)
python-version = "3.12"
# Source directories
[tool.ty.src]
include = ["src", "tests"]
exclude = ["src/generated/**", "*.proto"]
# Python environment (auto-detected from .venv if not set)
[tool.ty.environment]
root = ["./src"]
python = "./.venv"
# Rule severity configuration
[tool.ty.rules]
# Make warnings errors
possibly-missing-attribute = "error"
possibly-missing-import = "error"
# Downgrade errors to warnings
division-by-zero = "warn"
# Disable specific rules
redundant-cast = "ignore"
unused-ignore-comment = "ignore"
# Override rules for specific files
[[tool.ty.overrides]]
include = ["tests/**"]
[tool.ty.overrides.rules]
unresolved-reference = "warn"
# Terminal output
[tool.ty.terminal]
error-on-warning = false # exit code 1 if warnings exist
output-format = "full" # full | concise | github | gitlab
Option 2: ty.toml (Alternative)
Create backend/ty.toml (same structure, no [tool.ty] prefix):
python-version = "3.12"
[src]
include = ["src", "tests"]
[rules]
possibly-unresolved-reference = "warn"
Important Rules
Error-Level (Default)
These will fail your CI/CD:
| Rule | What it catches |
|---|---|
call-non-callable |
Calling non-callable objects: 4() |
division-by-zero |
Division by zero: 5 / 0 |
unresolved-import |
Missing modules: import nonexistent |
unresolved-reference |
Undefined variables: print(undefined_var) |
unresolved-attribute |
Missing attributes: obj.missing_attr |
invalid-argument-type |
Wrong arg types: func(x: int) called with func("str") |
invalid-return-type |
Return type mismatch |
missing-argument |
Missing required args: func(x: int) called as func() |
unknown-argument |
Unknown kwargs: func(x=1, unknown=2) |
unsupported-operator |
Bad operators: "string" + 123 |
invalid-assignment |
Type mismatch: x: int = "string" |
Warning-Level (Default)
Won't fail CI unless you enable --error-on-warning:
| Rule | What it catches |
|---|---|
possibly-unresolved-reference |
Variables that might not be defined (conditional) |
possibly-missing-attribute |
Attributes that might not exist (conditional) |
possibly-missing-import |
Imports that might be missing (conditional) |
redundant-cast |
Unnecessary cast() calls |
deprecated |
Usage of deprecated APIs |
undefined-reveal |
reveal_type() without importing it |
Ignore-Level (Disabled by Default)
Must explicitly enable:
| Rule | What it catches |
|---|---|
unused-ignore-comment |
Unused # type: ignore or # ty: ignore |
possibly-unresolved-reference |
Possibly undefined refs in conditional code |
division-by-zero |
Preview rule - division by zero |
Suppression Comments
ty-specific suppression
# Suppress specific rule
result = unsafe_operation() # ty: ignore[invalid-argument-type]
# Suppress multiple rules
value = risky() # ty: ignore[unresolved-attribute, invalid-return-type]
# Multi-line expressions (comment on first OR last line)
result = long_function( # ty: ignore[missing-argument]
arg1,
arg2
)
# Combine with other tools
x = 1 # ty: ignore[division-by-zero] # fmt: skip
Standard type: ignore (PEP 484)
# ty respects standard type: ignore
result = something() # type: ignore
# But ty: ignore is preferred for specificity
result = something() # ty: ignore[invalid-return-type]
Disable all checking in a function
from typing import no_type_check
@no_type_check
def untyped_function():
return "anything" + 123 # no errors
Check for unused suppressions
[tool.ty.rules]
unused-ignore-comment = "warn" # warn about unused suppressions
Common Configurations for Production
Strict Mode (Recommended)
[tool.ty.rules]
# Treat all "possibly" rules as errors
possibly-missing-attribute = "error"
possibly-missing-import = "error"
possibly-unresolved-reference = "error"
# Catch unused suppressions
unused-ignore-comment = "warn"
# Stricter terminal behavior
[tool.ty.terminal]
error-on-warning = true
Gradual Adoption (Recommended for existing codebases)
[tool.ty.rules]
# Downgrade strict rules to warnings
unresolved-attribute = "warn"
invalid-argument-type = "warn"
# Focus on critical errors only
[tool.ty.terminal]
error-on-warning = false
FastAPI-Specific
[[tool.ty.overrides]]
include = ["src/routes/**", "src/dependencies/**"]
[tool.ty.overrides.rules]
# FastAPI uses runtime dependency injection
unresolved-reference = "warn" # for Depends() params
CLI Flags Reference
Rule Control
# Override rule severity
ty check --error possibly-unresolved-reference
ty check --warn division-by-zero
ty check --ignore redundant-cast
# Can combine multiple
ty check --error rule1 --warn rule2 --ignore rule3
Environment
# Specify Python environment
ty check --python .venv
# Python version
ty check --python-version 3.12
# Platform
ty check --python-platform linux
ty check --python-platform all # no platform assumptions
Output Control
# Verbosity
ty check -v # verbose
ty check -vv # very verbose
ty check -q # quiet
ty check -qq # silent
# Exit codes
ty check --exit-zero # always exit 0
ty check --error-on-warning # warnings = exit 1
Environment Variables
# Log level (for debugging ty itself)
TY_LOG=debug ty check
TY_LOG=trace ty check
# Parallelism limit
TY_MAX_PARALLELISM=4 ty check
# Profile performance
TY_LOG_PROFILE=1 ty check # creates tracing.folded
# Python path (additional search paths)
PYTHONPATH=/extra/path ty check
# Virtual environment
VIRTUAL_ENV=/path/to/.venv ty check
Integration
CI/CD (GitHub Actions)
name: Type Check
on: [push, pull_request]
jobs:
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- name: Install dependencies
run: uv sync --all-extras
- name: Type check
run: uv run ty check --output-format github
Pre-commit Hook
# .pre-commit-config.yaml
repos:
- repo: https://github.com/astral-sh/ty
rev: v0.0.1 # use latest version
hooks:
- id: ty
VS Code
// .vscode/settings.json
{
"python.linting.enabled": true,
"python.linting.tyEnabled": true,
"python.linting.tyArgs": ["check"],
}
Just/Makefile
# Makefile
.PHONY: typecheck
typecheck:
ty check
.PHONY: typecheck-watch
typecheck-watch:
ty check --watch
ty vs mypy vs pyright
| Feature | ty | mypy | pyright |
|---|---|---|---|
| Speed | 🚀 10-100x faster | Baseline | Fast (but slower than ty) |
| Language | Rust | Python | TypeScript |
| Config | Minimal (auto-detects) | Verbose | Verbose |
| Strictness | Configurable | Very strict | Very strict |
| IDE Support | Growing | Excellent | Excellent (VSCode) |
| Ecosystem | New (2024) | Mature (2012) | Mature (2019) |
| Plugin Support | Limited | Extensive | Limited |
| Adoption | Early | Industry standard | Microsoft standard |
Migration from mypy
ty is mostly compatible with mypy. You can run both in parallel:
[tool.ty.rules]
# Map mypy behavior to ty
invalid-argument-type = "error" # mypy: arg-type
invalid-return-type = "error" # mypy: return-value
unresolved-attribute = "error" # mypy: attr-defined
Key differences:
- ty is faster but less mature
- mypy has more plugins (e.g., sqlalchemy, django)
- ty auto-detects more (less config needed)
- ty focuses on speed, mypy on completeness
Recommendation: Use ty in dev for fast feedback, keep mypy in CI for comprehensive checks (for now).
Troubleshooting
ty can't find my virtual environment
# Explicitly specify
ty check --python .venv
# Or in pyproject.toml
[tool.ty.environment]
python = "./.venv"
False positives in generated code
[tool.ty.src]
exclude = ["src/generated/**", "alembic/versions/**"]
ty is too strict
# Downgrade specific rules
[tool.ty.rules]
possibly-missing-attribute = "warn"
possibly-unresolved-reference = "warn"
Performance profiling
# Generate flamegraph
TY_LOG_PROFILE=1 ty check
# View with flamegraph.pl or speedscope.app
Best Practices for This Project
1. Use ty for fast local development
# Quick checks while coding
ty check --watch
2. Keep mypy for CI completeness
# Both in CI
- run: ty check # fast, catches most issues
- run: mypy src/ # thorough, catches edge cases
3. Suppress intentional violations
# FastAPI dependency injection
async def get_db(db: Annotated[AsyncSession, Depends(get_db_session)]):
# ty might not understand Depends()
return db # ty: ignore[invalid-return-type]
4. Configure for async/SQLAlchemy
[[tool.ty.overrides]]
include = ["src/repositories/**", "src/services/**"]
[tool.ty.overrides.rules]
# Async/SQLAlchemy patterns ty might not understand yet
unresolved-attribute = "warn"
Key Takeaways
✅ DO:
- Use
ty check --watchduring development - Configure
pyproject.tomlfor your project - Enable
unused-ignore-commentto keep suppressions clean - Use
--error-on-warningin CI for strictness
❌ DON'T:
- Blindly suppress errors (investigate first)
- Use
# type: ignorewithout rule codes - Disable important rules globally (use overrides)
- Expect feature parity with mypy (yet)
Quick Reference Card
# Development
ty check # check everything
ty check --watch # watch mode
ty check src/models/ # specific directory
# CI/CD
ty check --error-on-warning # warnings = errors
ty check --output-format github # GitHub annotations
# Debugging
ty check -vv # very verbose
TY_LOG=debug ty check # ty internal logs
# Configuration
ty check --python .venv # specify venv
ty check --python-version 3.12 # specify version
ty check --error rule-name # override rule severity
Resources
- Official Docs: https://docs.astral.sh/ty
- GitHub: https://github.com/astral-sh/ty
- Changelog: https://github.com/astral-sh/ty/releases
- Rule Reference: https://docs.astral.sh/ty/reference/rules
- Astral Blog: https://astral.sh/blog
Last Updated: 2025-12-06 ty Version: 0.0.1-alpha.30+ Maintained By: Astral (creators of uv, Ruff)