Kompatibilitätsprobleme und CI-Optimierung vor dem Umstieg auf TypeScript 7
2026年7月29日
0
Computing/SoftwareComments (0)
Log in to leave a comment
No posts yet
Log in to leave a comment
No posts yet
TypeScript 7 ist da, neu geschrieben in Go. Bei der Nachricht, dass die Typprüfungsgeschwindigkeit bis zu 12-mal schneller geworden ist, wollen Teams, die große Monorepos betreiben, wahrscheinlich sofort umsteigen. Tatsächlich berichteten Engineering-Teams bei Vanta und VS Code, dass sie die CI-Pipeline-Build-Zeiten um mehr als 80 % reduzieren konnten.
Aber eine unüberlegte Anwendung führt dazu, dass die gesamte CI abstürzt. Das liegt daran, dass durch den Wechsel zu nativen Binärdateien die Unterstützung für bestehende statische Analyse-APIs auf spätere Versionen verschoben wurde. Werkzeuge wie @typescript-eslint oder ts-morph, die interne APIs über require('typescript') inspizierten, funktionieren auf einen Schlag nicht mehr. Die Performance ist verlockend, aber ein Zerbrechen der Toolchain ist ein anderes Thema. Um Bereitstellungsausfälle zu verhindern und gleichzeitig von Geschwindigkeitsvorteilen zu profitieren, sind einige Workarounds erforderlich.
Die tsc-Binärdatei von TypeScript 7.0 blockiert Aufrufe interner Node.js-Module vollständig. Pakete, die wie bisher in einer JS-Umgebung importiert und verwendet wurden, werfen zum Build-Zeitpunkt sofort Fehler. Wer ohne Vorbereitung nur die Version anhebt, riskiert eine Lähmung der gesamten CI-Pipeline.
Es ist sicherer, ein Diagnoseskript ganz oben in der Pipeline einzubauen. Dieses scannt alle package.json- und tsconfig.json-Dateien im Workspace, um Optionen oder Pakete zu finden, die von TS 7 abgelehnt werden. Wenn veraltete Konfigurationen wie ignoreDeprecations oder target: es5 verbleiben, wird sofort ein Fehler ausgegeben und der Prozess gestoppt.
`javascript
// scripts/check-ts7-compatibility.mjs
import fs from 'node:fs';
import { globSync } from 'glob';
const INCOMPATIBLE_DEPS = [
'ts-morph',
'ts-node',
'@babel/plugin-transform-typescript',
'typescript-eslint',
'@typescript-eslint/parser'
];
const DEPRECATED_TSCONFIG_OPTIONS = ['target:es5', 'moduleResolution:node', 'baseUrl', 'ignoreDeprecations'];
function runDiagnostics() {
console.log('TypeScript 7 호환성 사전 진단 시작...');
let hasError = false;
const packageFiles = globSync('/package.json', { ignore: '/node_modules/' });
for (const file of packageFiles) {
const content = JSON.parse(fs.readFileSync(file, 'utf8'));
const allDeps = { ...content.dependencies, ...content.devDependencies };
for (const dep of INCOMPATIBLE_DEPS) {
if (allDeps[dep]) {
console.warn([의존성 경고] ${file}: '${dep}' 패키지는 TS7 네이티브 API와 호환되지 않습니다.);
hasError = true;
}
}
}
const tsconfigFiles = globSync('/tsconfig*.json', { ignore: '/node_modules/' });
for (const file of tsconfigFiles) {
const rawContent = fs.readFileSync(file, 'utf8');
for (const opt of DEPRECATED_TSCONFIG_OPTIONS) {
if (rawContent.includes(opt)) {
console.error([설정 오류] ${file}: 무효화된 옵션 발견 -> '${opt}');
hasError = true;
}
}
}
if (hasError) process.exit(1);
}
runDiagnostics();
`
Eigene AST-Transformator-Plugins, die auf ts.createProgram() oder ts.transform() angewiesen sind, funktionieren ebenfalls nicht. TS 7.0 akzeptiert keine Injektion externer JS-Plugins. Solche Logiken müssen durch Rust/C++-Binding-Module wie SWC oder Babel ersetzt oder aus dem Kompilierungsschritt herausgelöst werden.
| Compiler-Option | Verhalten in TypeScript 6.0 | Verhalten in TypeScript 7.0 | Lösungsansatz |
|---|---|---|---|
target |
Warnung bei Verwendung von es5 |
Hard Error (Abbruch der Kompilierung) | Änderung auf es2022 oder höher |
moduleResolution |
Einstellung node erlaubt |
Hard Error | Änderung auf bundler oder node16 |
baseUrl |
Einzelnutzung erlaubt | Hard Error | Umstellung auf ausschließliche Nutzung von compilerOptions.paths |
ignoreDeprecations |
Warnung ignorieren funktioniert | Option ungültig und Fehler | Vollständiges Löschen dieser Einstellung |
strict |
Standardwert false |
Standardwert true |
Explizite Wertsetzung in tsconfig.json |
TypeScript 7 nutzt das Threading-Modell von Go. Es bietet neue Optionen: --checkers für die parallele Typprüfung und --builders zur Steuerung von Projekt-Referenz-Builds. Das Engineering-Team von Slack kombinierte diese Optionen, um die Typprüfungszeit von 20 Minuten auf 4,5 Minuten zu verkürzen. Wenn man jedoch zu viele Threads im Vergleich zur Anzahl der Kerne ansetzt, steigt der CPU-Context-Switching-Overhead und der Build stürzt aufgrund von OOM (Out of Memory) ab.
Es ist ratsam, die Threads gemäß den Runner-Spezifikationen nach folgender Formel anzusetzen. Sei die Anzahl der zugewiesenen Kerne der virtuellen Maschine, der Gesamtspeicher, der für das Betriebssystem reservierte Speicher () und die durchschnittliche Speicherbelegung pro Worker (). Die optimale Thread-Anzahl für ein einzelnes Projekt ergibt sich wie folgt:
N_{checkers} = minleft( C_{vCPU}, leftlfloor rac{M_{total} - M_{OS}}{M_{worker}} ight floor ight)Die Summe aller Threads darf nicht überschreiten. Bei einer Runner-Umgebung mit beispielsweise 8 vCPUs / 16 GB RAM ist die Einstellung --checkers 4, --builders 2 angemessen.
`yaml
name: Monorepo Parallel Typecheck
on:
push:
branches: [main]
jobs:
typecheck:
runs-on: ubuntu-latest-8-core
steps:
- name: Checkout Codebase
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'pnpm'
- name: Install Dependencies
run: pnpm install --frozen-lockfile
- name: Purge Legacy TS 6.0 Cache
run: find . -name "*.tsbuildinfo" -not -path "*/node_modules/*" -delete
- name: Execute TS 7 Parallel Check
run: npx tsc --build --checkers 4 --builders 2 --verbose
`
Es gibt etwas zu beachten. Die inkrementelle Kompilierungs-Engine von TS 7 ist nicht kompatibel mit dem Format der alten .tsbuildinfo-Cachedateien aus TS 6. Wenn man den alten Cache vor dem Build nicht mit find . -name "*.tsbuildinfo" -delete löscht, kommt es zu einem Segmentierungsfehler.
Der Sprachserver für Editoren wurde ebenfalls zu einem auf einer Go-Binärdatei basierenden LSP umgestaltet. Laut AWS CodeBuild-Testergebnissen reduzierte sich die Zeit bis zum Anzeigen des ersten Typfehlers beim Öffnen einer Datei in einem großen Monorepo von 17,5 Sekunden auf 1,3 Sekunden.
Um die VS Code-Umgebung der Teammitglieder zu vereinheitlichen, kann die folgende Konfiguration in .vscode/settings.json im Stammverzeichnis des Monorepos eingefügt werden:
`json
{
"typescript.tsdk": "node_modules/typescript/lib",
"js/ts.experimental.useTsgo": true,
"typescript.enablePromptUseWorkspaceTsdk": true,
"typescript.preferences.preferTypeOnlyAutoImports": true,
"files.associations": {
"*.tsbuildinfo": "json"
}
}
`
Gelegentlich treten Konflikte mit Template-Sprachen-Plugins wie Vue oder Svelte auf, sodass die Syntaxanalyse fehlschlägt. In diesem Fall kann man über die Befehlspalette (Strg+Umschalt+P) auf TypeScript: Select TypeScript Version... klicken und vorübergehend auf die alte TS 6.0-Engine zurückwechseln.
Wenn Altlast-Pakete im Projekt verstrickt sind und ESLint nicht sofort auf die TS 7-Umgebung aktualisiert werden kann, lässt sich dies durch eine Dual-Engine-Konfiguration umgehen, die zwei Compiler parallel nutzt. Den statischen Analysewerkzeugen wird die TS 6-API zugewiesen, während die eigentliche Typprüfung und der Build der nativen TS 7-Binärdatei überlassen werden.
Man installiert beide Compiler gleichzeitig, indem man Paket-Aliase in der package.json angibt:
`json
{
"name": "monorepo-root",
"private": true,
"devDependencies": {
"typescript": "npm:@typescript/typescript6@^6.0.2",
"@typescript/native": "npm:typescript@^7.0.2",
"eslint": "^9.0.0",
"typescript-eslint": "^8.0.0"
},
"scripts": {
"typecheck": "ts-native --build",
"typecheck:legacy": "tsc6 --noEmit",
"lint": "eslint ."
}
}
`
In diesem Zustand ist es sicher, ein Shadow-Build-Skript in die CI einzubinden. Man kann die Diagnoseergebnisse des bisherigen TS 6 mit denen von TS 7 mittels diff vergleichen, um zu überwachen, ob sich die Ergebnisse bei Template-Literal-Typen oder bedingter Typ-Inferenz (Conditional Types) unterscheiden.
`bash
#!/usr/bin/env bash
set -e
echo "=== 1. 기존 TS 6.0 컴파일러 진단 출력 생성 ==="
npx tsc6 --noEmit --pretty false > ./ts6-baseline.log 2>&1 || true
echo "=== 2. 신규 TS 7.0 네이티브 컴파일러 진단 출력 생성 ==="
npx --package @typescript/native tsc --noEmit --pretty false > ./ts7-output.log 2>&1 || true
echo "=== 3. 진단 결과 Diff 대조 검증 ==="
DIFF_RESULT=$(diff ./ts6-baseline.log ./ts7-output.log || true)
if [ -z "DIFF_RESULT"
exit 0
fi
`
Bis die Kompatibilitätsprobleme der Toolchain behoben sind, ist es pragmatisch, Linter- und Typprüfungsrollen getrennt zu betreiben. Indem man fehleranfällige Optionen mit einem Vorab-Diagnoseskript bereinigt und die Threads an die CI-Infrastruktur-Spezifikationen anpasst, lässt sich der Geschwindigkeitsgewinn nutzen, ohne dass die Deployment-Pipeline zum Stillstand kommt.