Von esbuild bis vite-plugin-checker: Typsicherheit ohne Bremsklotz
Vite transpiliert TypeScript beim Entwickeln blitzschnell über esbuild, prüft dabei aber bewusst keine Typen, damit Hot Module Replacement und Dev-Server-Start schnell bleiben. Dieser Artikel zeigt, wie ihr mit vite-plugin-checker Typfehler trotzdem sichtbar macht, vite.config.ts sauber typisiert und ein minimales Vite plus TypeScript Setup für Admin-Widgets und Frontend-Tools aufsetzt.
Inhaltsverzeichnis
- 1. Warum Vite die richtige Wahl für TypeScript-Build-Pipelines ist
- 2. Wie Vite TypeScript verarbeitet: esbuild statt tsc
- 3. Die Kehrseite: Vite prüft während Dev-Server und HMR keine Typen
- 4. vite.config.ts typsicher konfigurieren: defineConfig und UserConfig
- 5. vite-plugin-checker: Typfehler im Dev-Overlay und Terminal sichtbar machen
- 6. tsconfig.json für Vite: isolatedModules und weitere Stolperfallen
- 7. Praxis-Setup: Ein minimales Vite plus TypeScript Projekt für ein Admin-Dashboard-Widget
- 8. Production-Build und CI/CD: Type-Checking zuverlässig absichern
- 9. Vite-Workflows im Vergleich: Dev-Server, Checker-Plugin und CI
- 10. Zusammenfassung
- 11. FAQ
1. Warum Vite die richtige Wahl für TypeScript-Build-Pipelines ist
Vite hat sich als Standardwerkzeug für moderne Frontend-Build-Pipelines etabliert, weil es zwei bislang widersprüchliche Ziele vereint: einen Dev-Server, der in Millisekunden startet, und einen Production-Build, der über Rollup hochoptimierten Code ausliefert. Für Teams, die neben PHP und Magento auch TypeScript-basierte Admin-Widgets, Storefront-Erweiterungen oder eigenständige Build-Skripte pflegen, reduziert Vite die Reibung zwischen Entwicklung und Deployment erheblich, weil dieselbe Konfigurationsdatei für beide Modi zuständig ist.
Der entscheidende Unterschied zu älteren Bundlern wie Webpack liegt in der nativen ESM-Nutzung während der Entwicklung: Der Browser lädt Module direkt, Vite transformiert nur bei Bedarf, und Hot Module Replacement bleibt auch bei wachsenden Projekten konstant schnell. Für TypeScript-Projekte bedeutet das konkret, dass Typprüfung und Modultransformation als getrennte Aufgaben behandelt werden, was zunächst ungewohnt wirkt, sich aber als bewusste Architekturentscheidung erweist, sobald klar ist, welches Werkzeug welche Aufgabe übernimmt.
2. Wie Vite TypeScript verarbeitet: esbuild statt tsc
Vite nutzt für die Transformation von TypeScript-Dateien während der Entwicklung nicht den offiziellen TypeScript-Compiler tsc, sondern esbuild, ein in Go geschriebenes Transpilations-Werkzeug, das zehn- bis hundertfach schneller arbeitet als tsc. esbuild entfernt dabei lediglich die Typannotationen aus dem Quellcode und übersetzt moderne Syntax in ein vom Browser oder Node.js verständliches Format, führt aber keinerlei Typprüfung durch. Diese Trennung ist kein Kompromiss, sondern eine bewusste Design-Entscheidung: Type-Checking ist von Natur aus ein Prozess, der die gesamte Programmstruktur analysieren muss, während reines Transpilieren pro Datei isoliert erfolgen kann und sich daher parallelisieren lässt.
Für Produktionsbuilds übernimmt Vite intern Rollup, das ebenfalls keine Typen prüft, sondern sich auf Bundling, Tree-Shaking und Code-Splitting konzentriert. Das bedeutet in der Praxis: Ein vite build kann erfolgreich durchlaufen, selbst wenn der Code Typfehler enthält, solange die Syntax gültig ist. Wer sich allein auf vite build als Qualitätssicherung verlässt, produziert also potenziell fehlerhafte Bundles, die erst zur Laufzeit auffallen.
3. Die Kehrseite: Vite prüft während Dev-Server und HMR keine Typen
Die Geschwindigkeit von esbuild hat einen unmittelbaren Nebeneffekt: Im Standard-Setup meldet der Vite-Dev-Server keinerlei TypeScript-Fehler im Browser oder Terminal. Ein falsch getippter Funktionsaufruf, ein fehlendes Property in einem Interface oder eine falsche Rückgabetyp-Annotation fällt weder beim Speichern noch beim Hot-Reload auf, solange der resultierende JavaScript-Code syntaktisch gültig bleibt. Für Entwickler, die von Webpack mit ts-loader oder von älteren Create-React-App-Setups kommen, ist das ein spürbarer Bruch mit gewohntem Verhalten, weil dort Typfehler traditionell den Build-Prozess sichtbar unterbrochen haben.
In der Praxis führt das dazu, dass Typfehler unbemerkt in den Git-Verlauf wandern und erst beim nächsten tsc-Aufruf, einem IDE-Neustart oder im schlimmsten Fall erst in der CI-Pipeline auffallen. Gerade bei kleineren Admin-Dashboard-Widgets, die selten vollständig durch die IDE typgeprüft werden, weil Entwickler primär im Browser mit laufendem Dev-Server arbeiten, entsteht so eine trügerische Sicherheit: Der Dev-Server läuft, also scheint alles in Ordnung, obwohl der Code an mehreren Stellen bereits inkonsistente Typen enthält.
4. vite.config.ts typsicher konfigurieren: defineConfig und UserConfig
Die Vite-Konfigurationsdatei selbst sollte als vite.config.ts angelegt und mit der Helper-Funktion defineConfig aus dem vite-Paket geschrieben werden, statt ein rohes Objekt zu exportieren. defineConfig ist zur Laufzeit fast eine Identitätsfunktion, ihr eigentlicher Wert liegt in der TypeScript-Signatur: Sie gibt der IDE vollständige Autovervollständigung und Typprüfung für sämtliche Konfigurationsoptionen, von server.proxy über build.rollupOptions bis zu plugin-spezifischen Erweiterungen.
Für komplexere Projekte, in denen sich die Konfiguration je nach Modus (development, production, test) unterscheidet, akzeptiert defineConfig alternativ eine Funktion, die ein ConfigEnv-Objekt mit mode und command entgegennimmt und ein UserConfig-Objekt zurückgibt. Diese Variante ist der sauberste Weg, um etwa unterschiedliche Base-Pfade für Entwicklung und Deployment in ein Magento-Static-Assets-Verzeichnis zu definieren, ohne prozessweite Umgebungsvariablen manuell auszulesen und dabei die Typsicherheit zu verlieren.
// vite.config.ts: typed configuration with mode-dependent options
import { defineConfig, type ConfigEnv, type UserConfig } from 'vite';
import path from 'node:path';
export default defineConfig(({ mode, command }: ConfigEnv): UserConfig => {
const isProd = mode === 'production';
return {
// Different base path for the built widget in a Magento static folder
base: isProd ? '/static/frontend/Mironsoft/default/en_US/widgets/' : '/',
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
build: {
target: 'es2022',
sourcemap: !isProd,
rollupOptions: {
output: {
entryFileNames: isProd ? '[name].[hash].js' : '[name].js',
},
},
},
server: {
port: 5173,
// Proxy admin API calls to a local Magento instance during development
proxy: {
'/rest': { target: 'https://mironsoft.test', secure: false, changeOrigin: true },
},
},
};
});
5. vite-plugin-checker: Typfehler im Dev-Overlay und Terminal sichtbar machen
vite-plugin-checker schließt genau die Lücke aus Abschnitt 3: Das Plugin startet den TypeScript-Compiler im Hintergrund in einem separaten Worker-Thread, parallel zum eigentlichen Vite-Dev-Server, und meldet gefundene Typfehler sowohl als Overlay direkt im Browser als auch als formatierte Ausgabe im Terminal. Weil die Prüfung asynchron in einem eigenen Prozess läuft, blockiert sie weder den Start des Dev-Servers noch die Reaktionszeit von Hot Module Replacement, wodurch der zentrale Geschwindigkeitsvorteil von esbuild vollständig erhalten bleibt.
Die Konfiguration erfolgt über eine einzige Plugin-Instanz in vite.config.ts, die typischerweise mit { typescript: true } aktiviert wird, optional erweiterbar um ESLint- oder Vue-Template-Prüfung im selben Overlay. In größeren Monorepos empfiehlt sich zusätzlich die Option typescript: { tsconfigPath: './tsconfig.json' }, um explizit zu steuern, welche Konfigurationsdatei geprüft wird, da Vite sonst nicht immer automatisch die richtige tsconfig.json in verschachtelten Workspace-Strukturen findet.
// vite.config.ts: surface type errors without blocking the dev server
import { defineConfig } from 'vite';
import checker from 'vite-plugin-checker';
export default defineConfig({
plugins: [
checker({
// Runs tsc in a separate worker thread, reports via overlay and terminal
typescript: {
tsconfigPath: './tsconfig.json',
},
// Optional: lint alongside type-checking in the same overlay
eslint: {
lintCommand: 'eslint "src/**/*.{ts,tsx}"',
useFlatConfig: true,
},
overlay: {
initialIsOpen: false,
},
}),
],
});
6. tsconfig.json für Vite: isolatedModules und weitere Stolperfallen
Vite verarbeitet jede Datei einzeln und isoliert, ohne Kenntnis des restlichen Typsystems, was in der tsconfig.json zwingend die Option "isolatedModules": true erfordert. Diese Einstellung verbietet TypeScript-Konstrukte, die nur mit vollständiger Programmanalyse korrekt transpiliert werden können, allen voran const enum und bestimmte Formen von Re-Exports reiner Typen ohne das Schlüsselwort export type. Wer diese Regel ignoriert, riskiert stillen, falschen JavaScript-Output, der zur Laufzeit unerwartete Werte liefert, ohne dass esbuild einen Fehler meldet.
Zusätzlich sollte "moduleResolution": "bundler" gesetzt werden, damit TypeScript dieselbe Modulauflösung wie Vite selbst simuliert, inklusive Unterstützung für Package-Exports-Felder ohne Dateiendungen. "skipLibCheck": true beschleunigt die Typprüfung zusätzlich spürbar, indem Deklarationsdateien aus node_modules nicht erneut geprüft werden, und "noEmit": true stellt sicher, dass tsc ausschließlich zur Prüfung verwendet wird, während Vite und esbuild allein für die tatsächliche Code-Ausgabe zuständig bleiben.
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"moduleResolution": "bundler",
"isolatedModules": true,
"verbatimModuleSyntax": true,
"strict": true,
"noUncheckedIndexedAccess": true,
"skipLibCheck": true,
"noEmit": true,
"resolveJsonModule": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src", "vite.config.ts"]
}
7. Praxis-Setup: Ein minimales Vite plus TypeScript Projekt für ein Admin-Dashboard-Widget
Für ein eigenständiges Admin-Dashboard-Widget, das später als statisches Bundle in ein Magento-Backend oder eine Hyvä-Storefront-Seite eingebunden wird, genügt eine schlanke Projektstruktur: ein src-Verzeichnis mit einer typisierten Einstiegsdatei, eine vite.config.ts mit Library-Modus-Konfiguration und eine tsconfig.json nach den Regeln aus Abschnitt 6. Der Library-Modus von Vite, aktiviert über build.lib, erzeugt statt einer vollständigen HTML-Anwendung ein einzelnes importierbares Bundle, das sich unabhängig vom Rest des Frontends versionieren und einbinden lässt.
Die Einstiegsdatei sollte konsequent typisiert sein, inklusive expliziter Rückgabetypen für exportierte Funktionen, da genau diese Signaturen später von vite-plugin-checker und in der CI-Pipeline geprüft werden. Für DOM-Zugriffe empfiehlt sich der Non-Null-Assertion-Operator nur dort, wo das Element garantiert existiert, ansonsten sind explizite Null-Checks vorzuziehen, um Laufzeitfehler in produktiven Admin-Widgets zu vermeiden, die häufig in unterschiedlichen Magento-Backend-Kontexten eingebunden werden.
// src/main.ts: typed entry module for an admin dashboard widget
interface OrderStat {
label: string;
value: number;
}
async function fetchOrderStats(): Promise<OrderStat[]> {
const response = await fetch('/rest/V1/mironsoft/dashboard/order-stats');
if (!response.ok) {
throw new Error(`Failed to load order stats: ${response.status}`);
}
return response.json() as Promise<OrderStat[]>;
}
function renderStats(container: HTMLElement, stats: OrderStat[]): void {
container.innerHTML = stats
.map((stat) => `<div class="stat"><span>${stat.label}</span><strong>${stat.value}</strong></div>`)
.join('');
}
function mountWidget(selector: string): void {
const container = document.querySelector<HTMLElement>(selector);
if (!container) {
// Explicit null check instead of a non-null assertion
console.warn(`Widget container "${selector}" not found`);
return;
}
fetchOrderStats()
.then((stats) => renderStats(container, stats))
.catch((error: unknown) => {
console.error('Order stats widget failed to load', error);
});
}
mountWidget('#order-stats-widget');
{
"name": "@mironsoft/order-stats-widget",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"typecheck": "tsc --noEmit",
"ci": "npm run typecheck && npm run build"
},
"devDependencies": {
"typescript": "^5.5.0",
"vite": "^5.4.0",
"vite-plugin-checker": "^0.7.0"
}
}
8. Production-Build und CI/CD: Type-Checking zuverlässig absichern
Weil vite build keine Typen prüft und vite-plugin-checker primär für die lokale Entwicklungserfahrung gedacht ist, gehört ein expliziter tsc --noEmit-Schritt in jede CI-Pipeline, unabhängig davon, ob GitHub Actions, GitLab CI oder ein anderes System zum Einsatz kommt. Dieser Schritt läuft synchron, bricht bei Typfehlern mit einem klaren Exit-Code ab und verhindert zuverlässig, dass fehlerhafter Code in den Main-Branch oder gar in ein Production-Deployment gelangt.
Ein sinnvolles package.json-Script trennt dafür typecheck explizit von build, sodass beide Schritte unabhängig voneinander in Pipeline-Stages ausgeführt und bei Bedarf parallelisiert werden können, was die Gesamtlaufzeit der Pipeline reduziert. In Projekten mit mehreren Admin-Widgets oder Micro-Frontends lohnt sich zusätzlich ein --incremental-Flag für tsc, das eine .tsbuildinfo-Datei zwischenspeichert und wiederholte CI-Läufe spürbar beschleunigt, ohne die Zuverlässigkeit der Prüfung zu beeinträchtigen.
9. Vite-Workflows im Vergleich: Dev-Server, Checker-Plugin und CI
Die drei in diesem Artikel vorgestellten Ansätze zur Typprüfung schließen sich nicht gegenseitig aus, sondern ergänzen sich in unterschiedlichen Phasen der Entwicklung. Die folgende Übersicht fasst zusammen, wann welcher Ansatz sinnvoll ist und worauf bei der Kombination zu achten ist.
| Ansatz | Geschwindigkeit | Wann Fehler sichtbar werden | Eignung für CI |
|---|---|---|---|
| Vite Dev-Server ohne Type-Checking | Sehr schnell (esbuild) | Nie automatisch, nur bei manueller Prüfung | Ungeeignet |
| Vite + vite-plugin-checker | Schnell, parallel im Worker | Sofort im Browser-Overlay und Terminal | Nicht allein ausreichend |
| Separates tsc --noEmit in CI | Langsamer, volle Programmanalyse | Erst bei Commit/Push in der Pipeline | Empfohlen als Gate |
In der Praxis hat sich die Kombination aus vite-plugin-checker für schnelles Feedback während der Entwicklung und einem separaten tsc --noEmit-Schritt als CI-Gate als robustester Ansatz etabliert. Der Dev-Server bleibt dabei durchgehend schnell, Typfehler werden trotzdem fast in Echtzeit sichtbar, und die CI-Pipeline garantiert zusätzlich, dass niemand einen lokal ignorierten Overlay-Hinweis versehentlich in den Main-Branch mergt.
Mironsoft
TypeScript-Tooling, Build-Optimierung und Frontend-Infrastruktur
Vite und TypeScript sauber aufsetzen?
Wir richten eure Vite-Pipeline typsicher ein, integrieren vite-plugin-checker für schnelles Feedback im Dev-Overlay und sichern den Production-Build mit einem verlässlichen tsc-Check in der CI-Pipeline ab.
Vite-Setup-Audit
Analyse von vite.config.ts, tsconfig.json und Build-Zeiten
Typsichere Konfiguration
defineConfig, UserConfig und vite-plugin-checker sauber integrieren
CI/CD-Absicherung
tsc --noEmit als verlässliches Type-Checking-Gate einrichten
10. Zusammenfassung
Die Kernfrage bei TypeScript mit Vite ist nicht, ob Typprüfung stattfindet, sondern wo. Vite selbst transpiliert TypeScript über esbuild und prüft dabei bewusst keine Typen, weder im Dev-Server noch beim Production-Build über Rollup. vite-plugin-checker schließt diese Lücke für die lokale Entwicklung, indem es TypeScript in einem separaten Worker-Thread parallel laufen lässt und Fehler nicht blockierend im Overlay und Terminal meldet. Für die tsconfig.json sind isolatedModules, moduleResolution: "bundler" und noEmit die drei zentralen Einstellungen, die Vites datei-isolierte Verarbeitung überhaupt erst zuverlässig ermöglichen.
Ein minimales Setup für ein Admin-Dashboard-Widget besteht aus einer typisierten vite.config.ts mit defineConfig und UserConfig, einer klar typisierten Einstiegsdatei und getrennten package.json-Scripts für dev, build und typecheck. Der entscheidende Sicherheitsnetz-Schritt bleibt ein separater tsc --noEmit-Aufruf in der CI-Pipeline, weil weder der Dev-Server noch der Production-Build allein garantieren, dass der ausgelieferte Code typkorrekt ist.
TypeScript mit Vite: Schnelle Build-Pipelines aufsetzen - Das Wichtigste auf einen Blick
esbuild statt tsc
Vite transpiliert während Dev-Server und Build über esbuild, ohne jemals Typen zu prüfen.
vite-plugin-checker
Parallele Typprüfung im Worker-Thread, Fehler im Browser-Overlay und Terminal, ohne den Dev-Server zu bremsen.
Typsichere Konfiguration
defineConfig und UserConfig für vite.config.ts, isolatedModules und noEmit in tsconfig.json.
CI als Gate
Separater tsc --noEmit-Schritt in der Pipeline, unabhängig von vite build.