DEV Community

Cover image for uv Now Type-Checks Python with ty. How to Set Up uv check (Pin ty First)
Alan West
Alan West

Posted on Originally published at blog.authon.dev

uv Now Type-Checks Python with ty. How to Set Up uv check (Pin ty First)

uv now has a type-check command. uv check syncs your project environment, downloads the ty type checker and runs it, with no config and no separate pip install. It also checks PEP 723 scripts against their own inline dependencies, which is the part that got me: your type checker has probably been checking those scripts against the wrong venv for years, flagging import requests as unresolved or silently using whatever happened to be installed.

I've been running it and I like it. The "your laptop and CI use the same ty version" pitch needs a footnote, though, and one command fixes it.

I'll go through projects first, then workspaces, scripts, VS Code, pre-commit and GitHub Actions.

What uv check actually does

Per the announcement from October 13, 2026, uv check prepares the project environment, installs its dependencies and checks the project with ty. You need uv 0.12.3 or later and ty 0.0.76 or later. ty itself gets downloaded the first time you run the command, so you don't install it yourself.

It's a preview feature. Astral says so plainly, and if you never run uv check, nothing about your uv workflow changes.

The CLI reference describes it as "Run checks on the project," and adds: "Currently, this type checks Python code using ty." There's also a --fix flag to apply safe fixes. Ruff is on the roadmap but not in the command yet. Frank Chu tested this by adding unused import os and import sys: uv check didn't report either of them. So keep your ruff check step for now.

Step 1: type-check a project with uv check

uv self update     # make sure you're on 0.12.3+
uv check
Enter fullscreen mode Exit fullscreen mode

That's the whole setup. On the first run in Chu's test (uv 0.13.5, ty 0.0.91, a 2 vCPU VM), uv created .venv, pulled a managed CPython, built the project and downloaded ty, which was 13.5 MiB. The cold run took 5.53s and warm runs took 0.71s. Bare ty check on the same tiny project took 0.09s, so ty barely registers and most of the time goes to uv syncing the environment.

The exit codes are what CI expects: 1 when there are diagnostics, 0 when the code is clean.

Every run prints an experimental warning. Chu found you can silence it with --preview-features check-command.

Step 2: workspaces with --package and --all-packages

If you use a uv workspace, the flags work the same way they do in uv sync:

uv check --package api        # one member: syncs it + its deps, checks only its files
uv check --all-packages       # every member in the workspace
Enter fullscreen mode Exit fullscreen mode

The reference docs say --package syncs the workspace environment with the selected members and their dependencies, and "only files owned by those members are checked." In a monorepo, that's what lets you run a quick per-package check in a pre-push hook and the full sweep in CI.

Step 3: check PEP 723 scripts against their own dependencies

This is the part I care about most. Say you have a script like this:

# /// script
# requires-python = ">=3.12"
# dependencies = ["requests"]
# ///
import requests

status: int = requests.get("https://example.com")
Enter fullscreen mode Exit fullscreen mode

Run this:

uv check --script fetch.py
Enter fullscreen mode Exit fullscreen mode

uv builds a separate environment from the script's own metadata and checks the script against it, even when it disagrees with the surrounding project. In Chu's test, plain ty check reported unresolved-import for requests because it was looking in the wrong venv. With the integration, it reported the real bug instead. I ran this exact file with uv 0.13.5, which pulled ty 0.0.92, and got the same thing, exit code 1:

error[invalid-assignment]: Object of type `Response` is not assignable to `int`
 --> fetch.py:7:15
Enter fullscreen mode Exit fullscreen mode

uv check on its own doesn't go looking for scripts, though. Chu had to name each one with --script. If you want ty to find and check the project's scripts in one pass, the blog gives an env var:

TY_UV=scripts ty check
Enter fullscreen mode Exit fullscreen mode

That setting is opt-in for a good reason. Astral's explanation: "As dependency installation can require execution of third-party Python code, automatic environment setup is currently opt-in." The same applies to uv check itself, so running uv check on a repo you just cloned is not a read-only operation. Treat it like pip install.

Step 4: ty in VS Code with the uv setting

Install the ty extension, make sure uv is on your PATH, and add this to settings.json:

{
  "ty.experimental.useUv": "on"
}
Enter fullscreen mode Exit fullscreen mode

With that set, ty runs uv in the background so completions, hover and Go to Definition inside a script use the script's declared requirements and not your project venv. If you write a lot of throwaway uv run scripts, this alone makes the integration worth trying.

Step 5: the ty pre-commit hook

The ty-pre-commit hook now runs uv check under the hood, and the rev tracks the ty version:

repos:
- repo: https://github.com/astral-sh/ty-pre-commit
  rev: v0.0.92  # ty version
  hooks:
    - id: ty
      args: [--isolated]
Enter fullscreen mode Exit fullscreen mode

I'd add --isolated right away. Without it, the hook can create or update your uv.lock and .venv during a commit, which is the kind of side effect that eventually costs you an afternoon. The README also suggests [--isolated, --no-python-downloads, --no-cache] if you want more idempotent runs.

Two other gotchas from the README:

  • The hook always checks the whole project. Don't set pass_filenames: true, because the hook errors if it receives filenames.
  • It doesn't work on pre-commit.ci, which blocks network access. Add ci: skip: [ty] there and run the check in your own CI.

Step 6: uv check in GitHub Actions

The ty-pre-commit README uses astral-sh/setup-uv@v10.3.2. Here's a minimal GitHub Actions job built on that:

typecheck:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v7.0.1
    - uses: astral-sh/setup-uv@v10.3.2
    - run: uv check --locked --all-packages
Enter fullscreen mode Exit fullscreen mode

--locked makes the job fail if uv.lock is out of date, so CI can't silently re-resolve and check against different dependencies than the ones you committed.

The catch: uv check only pins ty if you add it

The announcement says uv check "automatically uses the ty version recorded in your project's lockfile," so laptops and CI agree. That only holds once ty is actually in your lockfile.

According to the --ty-version docs, the exact version from uv.lock is used "when ty is a project dependency or a dependency in the project's dev group." If it isn't, uv uses a constrained range like >=0.0,<0.1. ty is at 0.0.9x and ships often, so in that range your laptop and your CI runner can end up on different ty builds, which is exactly the drift the feature is supposed to prevent.

Chu checked this: after uv add --dev "ty==0.0.80", uv check -v used 0.0.80 even though 0.0.91 was out. So the fix is one command:

uv add --dev ty
Enter fullscreen mode Exit fullscreen mode

I did this on a fresh uv init project and uv check -v logged Requirement already installed: ty==0.0.92, the version now in the lockfile.

Do this before you add uv check to CI. I'd argue it should be the default, or at least come with a loud warning.

Should ty replace mypy or Pyright yet?

Not yet, and Astral's post doesn't claim it should. The integration is experimental, ty is still on 0.0.x, and Astral says it is "continuing toward ty's stable release." ty's docs claim it's 10x to 100x faster than mypy and Pyright. I believe the speed claim. What I don't know yet is how many of your existing # type: ignore comments and plugin-dependent patterns (Django, SQLAlchemy, pydantic mypy plugins) will survive the switch.

What I'm doing for now:

Situation My call
PEP 723 scripts, one-off tools Use uv check --script now. mypy never handled these well.
New project, no mypy plugins Run uv check in CI next to your current checker for a few weeks
Large codebase on mypy plugins Keep mypy. Try ty in the editor only.
Monorepo on uv workspaces Trial --all-packages in a non-blocking CI job

Running it next to your current checker costs you about a minute of CI setup. Dropping your current checker for a 0.0.x preview is a bet I'm not making on a production codebase.

So where are you on this? Would you replace mypy or pyright with ty today, or wait until it's out of preview? If you've already switched, tell me which false positives made you want to switch back.

Sources

Top comments (1)

Collapse
 
humam_moin profile image
Humam Moin •

I’ve been using uv for my Python projects, and this looks like a useful addition. I like that it handles dependencies and type checking together. The tip about pinning ty before using it in CI is really helpful!