
Ty
- 17 installs
- 25 repo stars
- Updated July 31, 2026
- hyperb1iss/hyperskills
Type-checks Python with ty, Astral's Rust-based type checker and language server reported 10-100x faster than mypy and Pyright.
About
Covers type checking Python code and setting up an LSP with ty. A developer uses it for ty check/server, resolving type errors, or configuring [tool.ty] in a project.
- Rust-based, 10-100x faster than mypy/Pyright
- Beta (0.0.x), formerly Red-Knot, now astral-sh/ty
Ty by the numbers
- 17 all-time installs (skills.sh)
- +1 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #190 of 290 Python skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/hyperb1iss/hyperskills --skill tyAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 17 |
|---|---|
| repo stars | ★ 25 |
| Last updated | July 31, 2026 |
| Repository | hyperb1iss/hyperskills ↗ |
What it does
Type-checks Python with ty, Astral's Rust-based type checker and language server reported 10-100x faster than mypy and Pyright.
Files
ty: Python Type Checker & Language Server
ty (v0.0.34, May 2026) is Astral's Rust-based Python type checker and language server. Beta status: 0.0.x versioning, breaking changes between versions, and no stable API yet. Astral reports 10-100x faster checks than mypy and Pyright on large projects.
Formerly "Red-Knot", renamed May 2025, extracted from the ruff repo to astral-sh/ty.
When to Use ty
[tool.ty]section inpyproject.tomlorty.tomlexists- Type checking Python code in any project
- Setting up an LSP for Python in your editor
When to wait: Projects heavily dependent on mypy plugins (Pydantic, Django, SQLAlchemy). ty has no plugin system and no plans to add one, first-class framework support is the stated approach instead.
How to Invoke
uvx ty check # One-off (latest)
uv run ty check # Project dependency
ty check # Global installCLI Commands
# Type checking
ty check # Check current directory
ty check path/to/file.py # Specific file
ty check src/ tests/ # Multiple directories
# Rule severity
ty check --error unresolved-import
ty check --warn division-by-zero
ty check --ignore unresolved-attribute
# Python targeting
ty check --python-version 3.12
ty check --python-platform linux
ty check --python .venv/bin/python
# Output formats
ty check --output-format full # Rich diagnostics (default)
ty check --output-format concise # One line per error
ty check --output-format github # GitHub Actions annotations
ty check --output-format gitlab # GitLab Code Quality
ty check --output-format junit # JUnit XML
# Watch mode
ty check --watch # Re-check on file changes
ty check -W # Short form
# Migration helper
ty check --add-ignore # Auto-add ty: ignore comments for all errors
# Introspection
ty explain rule # List all rules
ty explain rule invalid-assignment # Explain specific rule
# Language server
ty server # Start LSPExit Codes
| Code | Meaning |
|---|---|
0 | No errors (warnings don't count unless --error-on-warning) |
1 | Type errors found |
2 | CLI/configuration error |
Configuration
ty reads from ty.toml (takes precedence) or pyproject.toml under [tool.ty].
[tool.ty.environment]
python-version = "3.12" # 3.7-3.15 allowed; unset falls back to 3.14
python-platform = "linux" # win32|darwin|android|ios|linux|all
python = ".venv" # Path to environment/interpreter
root = ["src"] # First-party module discovery
extra-paths = [] # Additional resolution paths
[tool.ty.rules]
unresolved-import = "error"
division-by-zero = "ignore"
possibly-unresolved-reference = "warn"
[tool.ty.analysis]
allowed-unresolved-imports = ["mypackage._internal.*"]
replace-imports-with-any = ["legacy_lib.*"]
respect-type-ignore-comments = true
[tool.ty.src]
include = ["src/**/*.py"]
exclude = ["**/migrations/**"]
respect-ignore-files = true
[tool.ty.terminal]
output-format = "full"
error-on-warning = falsePer-File Overrides
[[tool.ty.overrides]]
include = ["tests/**", "**/test_*.py"]
[tool.ty.overrides.rules]
possibly-unresolved-reference = "warn"
unresolved-attribute = "ignore"Suppression Comments
# Preferred: rule-specific
x = foo # ty: ignore[possibly-unresolved-reference]
# Broad (discouraged)
x = foo # ty: ignore
# Legacy (honored by default, configurable)
x = foo # type: ignoreRule: Fix type errors instead of suppressing. Only add ignore comments when explicitly requested. Always prefer rule-specific ignores.
What Makes ty Unique
Unknown vs Any
ty distinguishes between Any (deliberate opt-out) and Unknown (inferred gap). This is the "gradual guarantee", all code is checked, but unknowns are treated permissively rather than skipped entirely (mypy skips unannotated functions by default).
Intersection Types
ty supports A & B intersection types natively, not available in mypy or pyright.
Fine-Grained Incrementality
Built on Salsa (same framework as rust-analyzer). Changing one function re-parses only that function and its dependents, not the entire file. This powers sub-millisecond editor responses.
Performance
| Project | ty | pyright | mypy |
|---|---|---|---|
| home-assistant (cold) | 2.19s | 19.62s | 45.66s |
| PyTorch (cold) | 4.04s | 262.74s | — |
| PyTorch (incremental) | 4.7ms | 386ms | — |
Editor/LSP Setup
ty ships a full LSP with go-to-definition, find references, auto-complete with auto-import, rename, inlay hints, and hover.
VS Code: Install astral-sh.ty extension.
Neovim (>=0.11):
vim.lsp.config('ty', { settings = { ty = {} } })
vim.lsp.enable('ty')Neovim (<0.11):
require('lspconfig').ty.setup({ settings = { ty = {} } })Zed: Built-in, enable in settings:
{ "languages": { "Python": { "language_servers": ["ty", "ruff"] } } }PyCharm: Native support in 2025.3+.
Any LSP client: Run ty server and connect.
Integration with Ruff
ty and ruff are complementary:
| Tool | Role |
|---|---|
| ruff | Linting (style, correctness, imports) + formatting |
| ty | Type checking + language server |
ty has no strict mode for requiring annotations. Use ruff's ANN001/ANN201 rules instead. Both LSPs can run simultaneously in editors.
Current Limitations (Beta)
| Limitation | Impact | Workaround |
|---|---|---|
| No plugin system | No Pydantic/Django/SQLAlchemy plugins | Wait for first-class framework support |
| No strict mode | Can't require annotations | Use ruff ANN rules |
| No pre-commit hook | Must set up manually | uvx ty check in custom hook |
| No TypeVarTuple/Unpack | NumPy/tensor typing limited | Use mypy for these |
| No TypedDict functional syntax | TD = TypedDict("TD", ...) not supported | Use class syntax |
| Beta stability | Breaking changes between versions | Pin version, test upgrades |
| Script deps ignored | PEP 723 inline metadata not recognized | Run ty in project context |
| Limited monorepo support | No automatic multi-root discovery | Configure root paths manually |
For the full type system feature matrix, see references/type-system.md. For detailed migration tables from mypy/pyright, see references/migration.md.
Migration Strategy
Quick Start (Parallel Adoption)
1. Run ty check --add-ignore to auto-suppress all current errors as baseline 2. Add ty to CI as non-blocking alongside existing type checker 3. Gradually remove ty: ignore comments 4. Switch ty to blocking once comfortable
From mypy
mypy . -> ty check
mypy --strict . -> ty check --error-on-warning # (partial)
mypy -p mypackage -> ty check src/mypackage/ # paths, not modules
mypy --python-version 3.11 -> ty check --python-version 3.11From pyright
pyright . -> ty check
pyright path/to/file.py -> ty check path/to/file.pyAnti-Patterns
| Anti-Pattern | Fix |
|---|---|
Blanket # ty: ignore everywhere | Fix errors or use rule-specific ignores |
Using # type: ignore in new code | Use # ty: ignore[rule-name] |
| Expecting mypy plugin behavior | Check limitation table; wait for framework support if needed |
| Running ty on unannotated code expecting strictness | Add ruff ANN rules for annotation enforcement |
| Pinning to latest without testing | Pin version in CI, test upgrades deliberately |
What This Skill is NOT
- Not a replacement for
ty explain rule <name>for rule details - Not for linting or formatting (use ruff)
- Not for package management (use uv)
- Not a mypy drop-in replacement yet (plugin gap, beta stability)
Migration Guide: mypy/pyright to ty
Error Code Mapping
mypy -> ty
| mypy code | ty rule | Notes |
|---|---|---|
import-not-found | unresolved-import | |
attr-defined | unresolved-attribute | |
arg-type | invalid-argument-type | |
assignment | invalid-assignment | |
return-value | invalid-return-type | |
union-attr | possibly-missing-attribute | |
override | invalid-method-override | |
redundant-cast | redundant-cast | |
name-defined | possibly-unresolved-reference | |
call-arg | invalid-argument-type | Merged with arg-type |
pyright -> ty
| pyright code | ty rule |
|---|---|
reportMissingImports | unresolved-import |
reportGeneralClassIssues | Various specific rules |
reportMissingTypeStubs | unresolved-import |
reportOptionalMemberAccess | possibly-missing-attribute |
reportReturnType | invalid-return-type |
reportAssignmentType | invalid-assignment |
reportArgumentType | invalid-argument-type |
Configuration Mapping
mypy -> ty
| mypy option | ty equivalent |
|---|---|
python_version = "3.12" | environment.python-version = "3.12" |
ignore_missing_imports = true | rules.unresolved-import = "ignore" |
exclude = ["tests"] | src.exclude = ["tests/**"] (glob patterns) |
check_untyped_defs = true | Default behavior (always on) |
disallow_untyped_defs = true | Use ruff ANN001/ANN201 |
strict = true | No single flag; enable rules individually |
plugins = ["pydantic.mypy"] | Not supported |
warn_unused_ignores = true | Default behavior |
warn_redundant_casts = true | rules.redundant-cast = "warn" (default) |
| Per-module overrides | [[tool.ty.overrides]] with file globs |
pyright -> ty
| pyright option | ty equivalent |
|---|---|
pythonVersion = "3.12" | environment.python-version = "3.12" |
pythonPlatform = "Linux" | environment.python-platform = "linux" |
venvPath / venv | environment.python = ".venv" |
include = ["src"] | src.include = ["src/**/*.py"] |
exclude = ["tests"] | src.exclude = ["tests/**"] |
executionEnvironments | [[tool.ty.overrides]] |
typeCheckingMode = "strict" | No equivalent; configure rules individually |
# pyright: ignore | # ty: ignore |
Suppression Comment Mapping
| Tool | Syntax |
|---|---|
| mypy | # type: ignore[error-code] |
| pyright | # pyright: ignore[reportCode] |
| ty | # ty: ignore[rule-name] |
ty honors # type: ignore by default (configurable via analysis.respect-type-ignore-comments).
Step-by-Step Migration
Phase 1: Baseline (Day 1)
# Auto-suppress all current errors
ty check --add-ignore
# Verify it passes
ty checkThis adds # ty: ignore[...] comments to every line with a type error, giving you a clean baseline.
Phase 2: Parallel CI (Week 1-4)
# Run both, ty as non-blocking
- run: mypy .
- run: ty check || true # Non-blockingCompare outputs. Note differences in diagnostics.
Phase 3: Gradual Cleanup (Ongoing)
Remove # ty: ignore comments one module at a time. Fix the underlying type errors.
Phase 4: Switch (When Ready)
Replace mypy with ty in CI as the blocking check. Remove mypy config.
When to Migrate Now
- Pure Python projects with no mypy plugin dependencies
- Projects that need faster CI (10-60x speedup)
- Teams that want a better LSP experience
- New projects starting from scratch
When to Wait
- Heavy Pydantic plugin usage (first-class support coming)
- Django/SQLAlchemy plugin dependencies
- Need for
TypeVarTuple(NumPy/tensor typing) - Production environments requiring battle-tested stability
ty Type System Feature Matrix
Fully Implemented
Special Types
Any, None, NoReturn/Never, Literal[...], LiteralString, type[C], float/complex promotion, Final, @final, ClassVar, Annotated, Required/NotRequired/ReadOnly, Union/Optional
Generics
TypeVar (legacy + PEP 695 syntax), bounds, constraints, defaults, variance, ParamSpec (+ .args/.kwargs, defaults), Self, generic classes/functions/aliases
Protocols
Definition, generic protocols, structural subtyping, inheritance, @runtime_checkable, @property members (partial)
Type Narrowing
isinstance()/issubclass(), is None/is not None, identity checks, truthiness, assert, match statements, hasattr(), callable(), assignment, TypeIs/TypeGuard
Tuples
Heterogeneous, homogeneous, empty, mixed, indexing, slicing, subclasses, unpacking
NamedTuple
Class syntax, field access, defaults, read-only, inheritance, functional syntax
TypedDict
Class syntax, key access, Required/NotRequired/ReadOnly, inheritance, generic, recursive, structural assignability
Enums
Enum/IntEnum/StrEnum, Literal[Member], .name/.value inference, auto(), member()/nonmember(), exhaustiveness checking
Callables
Callable[[X, Y], R], gradual form, ParamSpec, callback protocols, assignability
Overloads
Resolution, generic, methods/constructors/static/classmethod
Dataclasses
All decorator params, field(), InitVar, ClassVar, KW_ONLY, replace(), asdict(), inheritance, generic
Unique to ty
| Feature | Description |
|---|---|
| Intersection types | First-class A & B — not available in mypy or pyright |
| Unknown vs Any | Explicit gradual typing: Any is deliberate, Unknown is inferred |
| Fixpoint iteration | Handles cyclic type dependencies |
| Reachability analysis | Detects unreachable code across version-specific branches |
| Fine-grained incrementality | Change one function -> only re-analyze that function + dependents |
Not Yet Implemented
| Feature | Impact | Tracking |
|---|---|---|
TypeVarTuple / Unpack | NumPy/tensor typing | #156 |
Concatenate | Supported in current beta | — |
type[SomeProtocol] | Protocol metaclass | #903 |
@classmethod/@staticmethod protocol members | Protocol completeness | #1381 |
ClassVar protocol members | Protocol completeness | #1380 |
| TypedDict functional syntax | TD = TypedDict("TD", ...) | #3095 |
PEP 728 closed/extra_items TypedDict | TypedDict completeness | #3096 |
Unpack for **kwargs | Typed kwargs | #1746 |
| Tuple length narrowing | Tuple refinement | #560 |
Enum functional syntax + Flag | Enum completeness | #876 |
| Overlapping overload diagnostics | Overload correctness | #103 |
dataclass_transform | Partial | — |
| Tagged union narrowing for TypedDict | Discriminated unions | #1479 |
Key Rules
| Rule | Default | Description |
|---|---|---|
unresolved-import | error | Module not found |
unresolved-attribute | error | Attribute not found on type |
invalid-assignment | error | Type mismatch in assignment |
invalid-argument-type | error | Wrong argument type |
invalid-return-type | error | Return type mismatch |
possibly-unresolved-reference | warn | May not be defined in all paths |
division-by-zero | ignore | Literal division by zero |
redundant-cast | warn | Unnecessary cast() call |
invalid-method-override | error | Override breaks Liskov substitution |
possibly-missing-attribute | warn | Attribute may not exist (union types) |