TypeScript mit Vite: Schnelle Build-Pipelines aufsetzen
<T>
type
TypeScript · Vite · Build-Tools · Frontend-Tooling
TypeScript mit Vite: Schnelle Build-Pipelines aufsetzen
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.

13 Min. Lesezeit esbuild · vite-plugin-checker · tsconfig Vite 5 · TypeScript 5 · Node 20

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.

11. FAQ: TypeScript mit Vite

1Warum prüft Vite während der Entwicklung keine TypeScript-Typen?
esbuild entfernt Typannotationen nur, statt sie zu prüfen. Type-Checking braucht projektweite Analyse und würde die Geschwindigkeit von esbuild zunichtemachen, deshalb bleibt es ein separater Schritt.
2Was macht vite-plugin-checker genau?
Startet tsc in einem separaten Worker-Thread parallel zum Dev-Server und meldet Typfehler als Browser-Overlay und Terminal-Ausgabe, ohne den Dev-Server zu blockieren.
3Verlangsamt vite-plugin-checker den Dev-Server?
Nein, die Prüfung läuft asynchron in einem eigenen Prozess. Dev-Server und HMR bleiben gleich schnell, Fehler erscheinen mit kleiner Verzögerung im Overlay.
4Wie unterscheidet sich defineConfig von einer einfachen Objekt-Exportkonfiguration?
defineConfig liefert vollständige Typinferenz und Autovervollständigung. Ein rohes Objekt ohne defineConfig erlaubt stille Tippfehler in Optionsnamen ohne verlässliche Prüfung.
5Was ist UserConfig und wozu wird es gebraucht?
Der Rückgabetyp von defineConfig, der alle gültigen Vite-Konfigurationsfelder beschreibt. Bei funktionsbasierter Konfiguration sollte er explizit annotiert werden.
6Warum verlangt Vite isolatedModules in der tsconfig.json?
Vite verarbeitet jede Datei isoliert ohne projektweite Typinformation. isolatedModules stellt sicher, dass jede Datei einzeln kompilierbar bleibt.
7Kann ich const enum in einem Vite plus TypeScript Projekt verwenden?
Nein, isolatedModules verbietet const enum. Ein normales enum oder ein Union-Type aus String-Literalen ist die gängige Alternative.
8Reicht vite-plugin-checker für die CI-Pipeline aus oder brauche ich zusätzlich tsc --noEmit?
Ein separater tsc --noEmit-Schritt in der CI bleibt notwendig, da vite-plugin-checker primär für die lokale Entwicklung gedacht ist.
9Wie baue ich ein minimales Vite plus TypeScript Setup für ein Admin-Dashboard-Widget?
Src-Verzeichnis mit typisierter Einstiegsdatei, vite.config.ts im Library-Modus über build.lib und eine tsconfig.json mit isolatedModules, moduleResolution bundler und noEmit.
10Welche esbuild-Limitierungen sollte ich bei TypeScript in Vite kennen?
Keine Typprüfung, kein const enum ohne isolatedModules-Verletzung, teils abweichendes Verhalten bei experimentellen Decorator-Metadaten. Ein separater tsc-Schritt oder vite-plugin-checker gleicht das aus.