Ein Dependabot-Pull-Request hob TypeScript von 6.0.3 auf 7.0.2. Auf den ersten Blick ist das ein gewöhnliches Major-Update. Für ein Astro-Projekt kann es aber bedeuten, dass astro check nicht mehr durchläuft.

In einem öffentlichen Astro/Starlight-Issue stieß ich auf zwei sehr unterschiedliche Lösungsversuche. Einer entfernte astro check aus der CI, damit der fehlschlagende Prüfschritt nicht mehr ausgeführt wird. Der andere setzte TypeScript auf 6 zurück, behielt die Typprüfung bei und hinderte Dependabot vorerst daran, erneut ein Update auf 7.x vorzuschlagen. Gemergt wurde die zweite Variante.

Dahinter steckt mehr als eine zu enge Versionsangabe in den Peer-Abhängigkeiten. TypeScript 7.0 ist der Übergang zu einem nativen Compiler; bestimmte Framework-Werkzeuge sind weiterhin auf die alte programmatische TypeScript-API angewiesen. In der offiziellen Ankündigung zu TypeScript 7.0 rät Microsoft ausdrücklich, für in andere Sprachen eingebettete TypeScript-Workflows wie Astro, Vue, Svelte und MDX zunächst bei TypeScript 6 zu bleiben.

Zur Einordnung der Belege: Ich habe die Dateien eines festgelegten Commits, die beiden PRs und den GitHub-Actions-Lauf des ursprünglichen Projekts geprüft. Zusätzlich habe ich fünf selbst entwickelte synthetische Node.js-Tests ohne Fremdabhängigkeiten ausgeführt, um den Unterschied zwischen „installiert“ und „auflösbar“ bei Package-exports zu untersuchen. Ich habe weder npm install noch astro check im betroffenen Astro-Projekt ausgeführt. Der unten gezeigte erfolgreiche CI-Lauf ist ein Ergebnis des ursprünglichen Projekts, kein von mir unabhängig reproduzierter Astro-Test.

Zwei Reparaturversuche: Den Check streichen oder die kompatible Version wiederherstellen

Der Vorfall steht in vishwakarma Issue #8. Zuvor hatte Dependabot PR #3 TypeScript auf 7.0.2 aktualisiert. Der Maintainer meldete daraufhin ein Kompatibilitätsproblem mit astro check.

PR #9 schlug vor, die Prüfung zu entfernen. Im apps/web/package.json sollten das Skript check und @astrojs/check entfallen. Auch typecheck-web sollte aus dem Makefile verschwinden, sodass check-web nur noch Formatierung und Build ausführt. Dieser PR wurde geschlossen und nicht gemergt.

Eine grüne CI ist leicht zu erreichen, wenn man die rote Prüfung einfach nicht mehr startet. Das behebt aber nicht die Typprüfung. astro build ist kein Ersatz für die Diagnosen, die astro check für .astro-Templates und TypeScript-Code liefert. Wer den Check weglässt, entfernt eine Qualitätsschranke, die das Projekt zuvor besaß.

Der tatsächlich gemergte PR #10 behielt die Prüfung bei und änderte stattdessen die TypeScript-Abhängigkeit im Web-Workspace:

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

Im festgelegten Commit nach dem Fix nennt apps/web/package.json Astro ^7.2.10 und @astrojs/check ^0.9.10. Die zugehörige package-lock.json enthält für @astrojs/[email protected] folgende Peer-Anforderung:

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

Das ist eine direkt aus der Lockdatei gelesene Deklaration, keine npm-Fehlermeldung aus einem eigenen Lauf. Für genau diese Checker-Version liegt TypeScript 7 außerhalb des angegebenen unterstützten Bereichs.

Es gibt auch einen realen CI-Beleg nach der Reparatur. Der GitHub-Actions-Lauf #38103905437 wurde für den PR-Head 5d828e8d55c5db833f5cca17f28da3b6aba05a99 ausgeführt. Im Web-Job waren make setup-web und make check-web erfolgreich, ebenso der Python-Job. Das belegt den Zustand nach dem Fix mit weiterhin aktiver Typprüfung. Es belegt nicht, dass ich den fehlschlagenden TypeScript-7-Zustand selbst nachgestellt hätte.

Warum kann ein installiertes TypeScript als „nicht installiert“ erscheinen?

Hier greifen zwei verschiedene Ebenen der Inkompatibilität ineinander.

Die grundlegendere betrifft die Compiler-API. TypeScript 7.0 wurde als nativer, in Go implementierter Compiler veröffentlicht. Dass tsc als Kommandozeilenprogramm läuft, bedeutet nicht, dass die stabile programmatische API aus TypeScript 6 weiterhin bereitsteht. Astro muss TypeScript innerhalb von .astro-Dateien über seine Sprachwerkzeuge analysieren. Ein neuer Compiler allein macht diese Integration nicht automatisch kompatibel.

Die zweite Ebene führt beim Debugging besonders leicht in die Irre. Das Astro-Upstream-Issue #17268 beschreibt, wie geänderte Package-exports von TypeScript 7 ältere Aufrufe wie require.resolve('typescript') mit ERR_PACKAGE_PATH_NOT_EXPORTED scheitern lassen können. Wenn eine Erkennung sämtliche Auflösungsfehler pauschal als „TypeScript fehlt“ behandelt, bekommt der Anwender einen irreführenden Hinweis.

Diesen begrenzten Node.js-Mechanismus wollte ich selbst überprüfen, ohne das ganze Astro-Projekt aufzubauen. Deshalb habe ich mit den Standardbibliotheken von Node.js v22.16.0 zwei kurzlebige Mock-Pakete erzeugt: eines mit einem normalen alten Einstiegspunkt und eines, das nur ./package.json exportiert, aber nicht den Root-Einstieg. Der relevante Teil der tatsächlich erzeugten Testkonfiguration, leicht umformatiert:

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

Für dieses synthetische Paket wirft require.resolve('native-ts-synthetic') den Fehler ERR_PACKAGE_PATH_NOT_EXPORTED, obwohl die Dateien vorhanden sind. Weil der Mock ./package.json ausdrücklich exportiert, lässt sich native-ts-synthetic/package.json dagegen auflösen. Bei einem tatsächlich fehlenden Paket liefern beide Wege MODULE_NOT_FOUND.

Ausgeführt wurde node --test ./node-exports-resolution.test.cjs unter Linux mit Node.js v22.16.0, ohne fremde npm-Pakete. Hier ein Ausschnitt aus dem wirklichen Node-TAP-Protokoll, nicht aus einer Astro-Ausführung:

# 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

Alle fünf Tests waren erfolgreich. Bewiesen ist damit ein bestimmtes Verhalten der Node.js-Paketauflösung, nicht die Funktionsfähigkeit oder der Fehler von TypeScript 7, Astro oder astro check. Auch der Ausweg über package.json ist keine allgemeingültige Reparatur: Ein Paket, das diesen Unterpfad nicht exportiert, kann ihn ebenfalls zurückweisen.

Selbst eine korrekte Erkennung installierter Pakete ersetzt keine in TypeScript 7.0 fehlende alte Compiler-API. Astros PR #18053, im September 2026 gemergt, verbesserte die Fehlermeldung für TypeScript 7. Das Schließen des zugehörigen Issues darf nicht mit vollständiger TypeScript-7.0-Kompatibilität gleichgesetzt werden.

Eine betroffene Astro-CI wiederherstellen

Bei einer vergleichbaren Astro-Version würde ich zunächst feststellen, welche TypeScript-Version und welches @astrojs/check tatsächlich installiert sind, was die Lockdatei als Peer-Bereich angibt und welcher Fehler konkret erscheint. Ein Dependabot-Vorschlag ist keine Garantie, dass das gesamte Framework-Tooling bereits kompatibel ist.

Das Beispielprojekt enthält einen npm-Workspace namens web im Verzeichnis apps/web. Die folgenden Schritte sind Befehle, die Leser in ihrem eigenen Reparatur-Branch ausführen können, abgeleitet aus dem öffentlichen PR #10. Ich habe sie nicht im Originalprojekt ausgeführt:

# Installierte Abhängigkeiten prüfen
npm ls typescript @astrojs/check -w web

# Für diesen konkreten Fall die kompatible Version wiederherstellen
npm install --save-dev 'typescript@^6.0.3' -w web

# Aus der Lockdatei neu installieren, Check und Build behalten
npm ci
npm run check -w web
npm run build -w web

Bei einem Versionskonflikt kann bereits npm ls mit einem Fehlercode enden; das ist eine nützliche Diagnose. Vorher den ursprünglichen Commit sichern oder einen separaten Branch verwenden und die tatsächlichen Node-/npm-Anforderungen sowie den Workspace-Namen prüfen. In einem Projekt ohne Workspaces lässt man -w web weg. ^6.0.3 ist die Entscheidung dieses fixierten Beispiels, keine ewige Empfehlung für sämtliche Astro-Projekte.

Anschließend sollte dasselbe automatische Major-Update nicht jede Woche erneut vorgeschlagen werden. Die gepinnte .github/dependabot.yml enthält unter dem npm-Eintrag folgende temporäre Regel:

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

Die ignore-Regel gehört zum betroffenen npm-Ökosystem. Unbeteiligte Abhängigkeiten sollen weiterhin Updates bekommen. Ebenso wichtig ist ein Termin oder eine technische Bedingung für die Aufhebung: Erst wenn Astos Checker und Sprachdienste den neuen Integrationsweg tatsächlich unterstützen und die eigene CI erfolgreich läuft, sollte die Sperre entfernt werden. Sonst wird eine Übergangslösung unbemerkt zum Dauerzustand.

Im Beispiel umfasst check-web Formatierung, astro check und Build. Zur Verifikation gehört deshalb mehr als ein grünes Gesamt-Badge: Stimmt die Lockdatei mit dem gewünschten Versionspaar überein? Ist die Installation über npm ci reproduzierbar? Wird astro check wirklich ausgeführt? Baut das Projekt weiterhin erfolgreich? Falls es bereits einen Testfall für einen erwarteten .astro-Typfehler gibt, sollte der Checker diesen weiterhin erkennen.

Wann kann man die Sperre für TypeScript 7.1 wieder entfernen?

Ein vorübergehendes Downgrade muss keine endgültige Architekturentscheidung sein. Microsoft sprach bei der TypeScript-7.0-Veröffentlichung bereits über die weitere Arbeit an programmatischen APIs und Möglichkeiten wie @typescript/typescript6, mit denen ältere Werkzeuge neben dem neuen tsc laufen können. Doch CLI-Compiler, Astro-Template-Analyse, Editor-Sprachdienste und CI-Typprüfung sind verschiedene Migrationsschritte.

Zum Zeitpunkt dieser Recherche war Astros Issue #17268 geschlossen und PR #18053 mit der verbesserten Fehlererklärung gemergt. Die Arbeit am TypeScript-7-Content-Mapper für .astro unter PR #17852 war dagegen noch offen. Das beschreibt nur den damaligen Quellenstand und garantiert nicht, was spätere Releases bereits unterstützen.

Wer nach einem TypeScript-Major-Update an astro check scheitert, sollte erst Fehlermeldung und Abhängigkeitsvertrag prüfen, die Typprüfung behalten, eine kompatible Kombination wiederherstellen und Lockdatei sowie CI testen. Die nächste Compiler-Migration kann ein eigener, kontrollierter Schritt sein. Es geht darum, die Prüfung wieder wirksam zu machen, nicht lediglich eine rote Pipeline stummzuschalten.

Warum ich Astro als Grundlage eines Entwicklerblogs gewählt habe, habe ich früher im Vergleich zwischen Astro, Markdown und GitHub Actions beschrieben. Hier geht es um die Zeit nach dieser Entscheidung: Ein automatisches Abhängigkeitsupdate sollte keine bereits vorhandene Qualitätsschranke stillschweigend abschaffen.