# Astro Check Breaks with TypeScript 7: Fix the Dependency, Don't Remove Type Checking

> Canonical: https://vxi.me/en/posts/astro-check-typescript-7-compatibility/
> Locale: en-us
> Published: 2026-10-11
> Updated: 2026-10-11
> Markdown: https://vxi.me/en/posts/astro-check-typescript-7-compatibility.md

A Dependabot pull request moved TypeScript from 6.0.3 to 7.0.2. That looks like a routine major-version upgrade. But in an Astro project, it can leave `astro check` unable to run.

While examining [a public Astro/Starlight issue](https://github.com/sivadotblog/vishwakarma/issues/8), I found two competing fixes. One proposal removed `astro check` so that CI would stop calling the failing tool. The other returned TypeScript to version 6, kept the checker, and temporarily stopped Dependabot from proposing 7.x again. **The second approach was merged.**

This is more than a peer-dependency version-range dispute. TypeScript 7.0 is a native compiler with different integration boundaries; some language tooling still relies on the older programmatic TypeScript APIs. Microsoft's [official TypeScript 7.0 announcement](https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/) explicitly advised embedded-language ecosystems, including Astro, Vue, Svelte and MDX, to remain on TypeScript 6 while their tooling catches up.

A note on evidence before getting into the fix: I inspected the pinned source files, both pull requests, and the project's GitHub Actions run. I also executed five dependency-free synthetic Node.js tests demonstrating how package `exports` can mislead an installation check. **I did not run `npm install` or `astro check` on the actual project.** The successful CI below is the original project's evidence, not an independent Astro reproduction.

## Two repairs: remove the checker, or restore a supported dependency

The incident appears in [vishwakarma Issue #8](https://github.com/sivadotblog/vishwakarma/issues/8). An earlier [Dependabot PR #3](https://github.com/sivadotblog/vishwakarma/pull/3) upgraded TypeScript to 7.0.2. The maintainer reported that `astro check` no longer worked with this combination.

[PR #9](https://github.com/sivadotblog/vishwakarma/pull/9) suggested cutting the checker out of the workflow. Its patch removed the `check` script and `@astrojs/check` package, then dropped `typecheck-web` from the Makefile so `check-web` would be reduced to formatting and building. That pull request was closed **without being merged**.

You can get a green build by ceasing to run a red check, but that is not the same as fixing type checking. `astro build` does not stand in for the diagnostics `astro check` provides for `.astro` templates and TypeScript. Removing the check quietly removes an existing quality gate.

The merged [PR #10](https://github.com/sivadotblog/vishwakarma/pull/10) kept that gate and instead restored the web workspace dependency:

```diff
- "typescript": "^7.0.2"
+ "typescript": "^6.0.3"
```

In the [fixed public commit](https://github.com/sivadotblog/vishwakarma/tree/9837d456647d948207941c86244e253594aef9ae), `apps/web/package.json` declares Astro `^7.2.10` and `@astrojs/check` `^0.9.10`. The pinned [`package-lock.json`](https://github.com/sivadotblog/vishwakarma/blob/9837d456647d948207941c86244e253594aef9ae/package-lock.json) identifies `@astrojs/check@0.9.10` and its peer requirement:

```json
"peerDependencies": {
  "typescript": "^5.0.0 || ^6.0.0"
}
```

That is a **declaration read from the lockfile**, not output from an npm command I ran. For this specific checker version, TypeScript 7 is outside the declared supported peer range.

The merged fix also has observable third-party CI evidence. [GitHub Actions run #38103905437](https://github.com/sivadotblog/vishwakarma/actions/runs/38103905437) ran on PR head `5d828e8d55c5db833f5cca17f28da3b6aba05a99`. Its Web job, including `make setup-web` and `make check-web`, and its Python job finished successfully. That supports the **post-fix state with the checker retained**. It does not mean I independently reproduced the failed TypeScript 7 side of the comparison.

## How can an installed TypeScript package appear to be missing?

There are two separate compatibility layers.

The deeper one is the compiler API. [TypeScript 7.0](https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/) moved its compiler to a native implementation. The `tsc` executable may work, but that alone does not supply the stable programmatic API surface that TypeScript 6 integrations used. Tools such as Astro need to analyze TypeScript *inside* `.astro` files and wire it into language services. A newer command-line compiler does not make those integrations compatible by itself.

The more misleading layer is package resolution. Astro's upstream [Issue #17268](https://github.com/withastro/astro/issues/17268) documented how TypeScript 7's package exports could make older detection logic using `require.resolve('typescript')` throw `ERR_PACKAGE_PATH_NOT_EXPORTED`. A tool that catches *every* resolution error and turns it into "TypeScript is not installed" can send developers down the wrong path.

I wanted to verify that narrower mechanism without installing Astro or the real TypeScript 7 package. With Node.js v22.16.0's standard library, I created two disposable mock packages: one with an ordinary legacy entry point, and another exporting only its `./package.json` subpath, not the package root. Here is the relevant configuration from the generated mock, formatted for readability:

```js
{
  name: 'native-ts-synthetic',
  version: '7.0.0-fixture',
  type: 'module',
  exports: {
    './package.json': './package.json'
  }
}
```

For that **synthetic package**, `require.resolve('native-ts-synthetic')` throws `ERR_PACKAGE_PATH_NOT_EXPORTED` even though the package files exist. Because the mock deliberately exports `./package.json`, that subpath resolves and reveals the installed package. A genuinely missing mock returns `MODULE_NOT_FOUND` for both paths.

I ran `node --test ./node-exports-resolution.test.cjs` in an isolated Linux environment using Node.js v22.16.0, with no third-party npm packages. A short extract from the **real Node TAP output**, not an Astro error log:

```text
# OBSERVED: legacy package resolution = FOUND
# OBSERVED: package exists; require.resolve(root) = ERR_PACKAGE_PATH_NOT_EXPORTED
# OBSERVED: require.resolve(package.json) = FOUND
# OBSERVED: absent package = MODULE_NOT_FOUND in both paths
# OBSERVED: catch-all = false missing; code-aware fallback = detected installed
# tests 5
# pass 5
# fail 0
```

Those five passing tests establish a particular **Node.js package-resolution behavior**. They do not test TypeScript 7, Astro, or an actual `astro check` invocation. The `package.json` fallback is not universal either: packages that do not export that subpath can reject it too.

Even a correct installed-package check cannot magically restore a missing TypeScript 7 programmatic API. Astro's [PR #18053](https://github.com/withastro/astro/pull/18053), merged in September 2026, improved the message shown for TypeScript 7; closing the related issue does not itself establish complete 7.0 compatibility.

## Restoring CI in an affected Astro project

For an affected version combination, start by checking the installed TypeScript version, the `@astrojs/check` version, the lockfile's peer range, and the actual checker error. Do not treat an automatic update recommendation as proof that the whole tooling stack has already migrated.

The example repository has an npm workspace named `web` under `apps/web`. These commands are **steps a reader can run on their own repair branch**, adapted from [the actual PR #10](https://github.com/sivadotblog/vishwakarma/pull/10). I did not execute them against this project:

```bash
# Inspect the dependency versions
npm ls typescript @astrojs/check -w web

# Restore the version used by this particular fix
npm install --save-dev 'typescript@^6.0.3' -w web

# Reinstall exactly from the lockfile and retain both checks
npm ci
npm run check -w web
npm run build -w web
```

A dependency conflict may make `npm ls` exit nonzero; that can be useful diagnostic evidence. Preserve the original commit or work on a new branch, check the project's actual Node/npm requirements, and verify its workspace name before running the commands. For a single-package repo, omit `-w web`. The `^6.0.3` range is what this pinned case used, **not a permanent recommendation for every Astro project**.

Then prevent the same automatic major update from repeatedly reopening the issue. The project's pinned [`.github/dependabot.yml`](https://github.com/sivadotblog/vishwakarma/blob/9837d456647d948207941c86244e253594aef9ae/.github/dependabot.yml) contains this npm-scoped temporary rule:

```yaml
- package-ecosystem: npm
  directory: /
  schedule:
    interval: weekly
  ignore:
    - dependency-name: typescript
      versions: [">=7.0.0"]
```

Put the rule under the **npm** configuration, not under a blanket ignore of unrelated dependencies. Write down when you intend to revisit it: after Astro's relevant checker and language tooling support the newer integration path, and after *your own* CI actually passes. Otherwise a temporary workaround can quietly become an indefinite version freeze.

In this example, `check-web` includes formatting, `astro check` and the build. A meaningful fix verification should check that the lockfile pins the intended combination, `npm ci` can reproduce the install, `astro check` actually runs, and the build still finishes. If the project already has a known `.astro` type-error fixture, verifying that the checker still catches it is stronger than relying solely on a green aggregate badge.

## Does TypeScript 7.1 mean the pin can be removed?

A temporary downgrade need not become the long-term architecture. Microsoft's original TypeScript 7.0 guidance discussed the continuing programmatic API work and options such as `@typescript/typescript6` that let older tools coexist with the newer `tsc`. But compiler command-line support, Astro template analysis, editor language services, and CI checking are distinct adoption tests.

At the time of this research, Astro [#17268](https://github.com/withastro/astro/issues/17268) was closed, the clearer error guidance from [PR #18053](https://github.com/withastro/astro/pull/18053) was merged, while the Content Mapper work for `.astro` and TypeScript 7 in [PR #17852](https://github.com/withastro/astro/pull/17852) was still open. These are time-specific source observations, not a guarantee of what later Astro or TypeScript 7.1 releases support.

For someone whose `astro check` just failed after a TypeScript major upgrade, the practical sequence is to inspect the error and dependency contract, **retain the checker**, restore a supported combination, verify the lockfile and CI, and schedule a separate migration assessment later. The goal is to recover real checking—not merely to stop a red CI job from making noise.

For background on why I chose this stack in the first place, see my earlier [Astro, Markdown and GitHub Actions comparison](https://aitokenhacks.com/posts/astro-vs-wordpress-markdown-github-actions-blog/). Here the question is what happens *after* choosing Astro, when the next automated dependency update threatens one of the safeguards the project already had.
