TypeScript: JavaScript mit Airbag

Vorwort (oder: „Warum TypeScript, wenn JavaScript doch läuft?“)

Klar läuft JavaScript. Genau wie ein Einkaufswagen mit schiefem Rad. Er kommt an. Irgendwann. Irgendwie. Mit Geräuschen.

TypeScript ist der Moment, in dem Du dem Einkaufswagen sagst: „Pass auf, Freundchen. Ab jetzt gelten Naturgesetze.“

TypeScript ist JavaScript + Typen. Nicht, weil Typen hip sind, sondern weil sie Dir Zeit sparen:

  • weniger Bugs
  • weniger „Warum ist das undefined?“
  • mehr Autocomplete
  • mehr Refactoring ohne Angstschweiß

Und vor allem: Coden macht mehr Spaß, wenn Du dem Editor beim Denken zuschauen kannst.

Dieses Dokument ist:

  • ausführlich
  • praxisnah
  • humorvoll (so gut es mit Bits und Bytes eben geht)
  • auf „IT-Enthusiasten“ zugeschnitten, nicht auf Lehrbuch-Opfer

Und ja: Wir bauen das am liebsten so, dass es sauber zu einem Spring-Boot-Backend passt.

Was TypeScript ist (und was nicht)

TypeScript ist …

  • ein Superset von JavaScript (alles JS ist gültiges TS)
  • ein Compiler, der Deine .ts/.tsx Dateien in JavaScript übersetzt
  • ein Werkzeug, das Fehler findet, bevor sie Dich nachts um 03:12 Uhr aus dem Bett klingeln

TypeScript ist nicht …

  • keine neue Laufzeitumgebung (am Ende läuft JavaScript)
  • kein magischer Schutz vor schlechtem Design (aber ein sehr guter Spiegel)
  • keine „echte“ Laufzeit-Typsicherheit (dazu später mehr)

Merksatz: TypeScript ist wie ein Sicherheitsgurt: Du merkst ihn erst, wenn er Dir den Tag rettet.

Setup: In 3 Minuten lauffähig

Option A: Minimal, ohne Framework

mkdir ts-spielwiese
cd ts-spielwiese
npm init -y
npm i -D typescript ts-node @types/node
npx tsc --init

Dann eine index.ts:

console.log("Hallo TypeScript!");

Start:

npx ts-node index.ts

Option B: UI-Projekt (praktisch): Vite + TypeScript

Wenn Du Web-UIs baust (und das wirst Du sehr wahrscheinlich):

npm create vite@latest esw-ui -- --template react-ts
cd esw-ui
npm i
npm run dev

Das ist der „ich will jetzt sofort was sehen“-Weg.

Das wichtigste Prinzip: Type Inference

TypeScript ist nicht „alles tippen bis die Finger bluten“.

Es kann sehr viel selbst ableiten:

const ovName = "Heusweiler";  // TypeScript weiß: string
const helfer = 42;            // TypeScript weiß: number
const aktiv = true;           // TypeScript weiß: boolean

Du schreibst Typen dort, wo sie wirklich helfen:

  • bei Funktionsgrenzen
  • bei Daten aus externen Quellen (z. B. API)
  • bei komplexen Objekten
  • bei öffentlichen Interfaces

Typen, die Du wirklich brauchst (und die Du lieben lernst)

1) Union Types: „Entweder oder“

type FunkStatus = "frei" | "belegt" | "ausfall";

const status: FunkStatus = "frei";
// const status2: FunkStatus = "kaputt"; // Fehler, gut so.

Das ist riesig für Zustände, Statusfelder, Rollen, Modusumschaltung.

2) Optional vs. Null vs. Undefined (die heilige Dreifaltigkeit)

type Meldung = {
id: string;
text?: string;       // optional: kann fehlen
ort: string | null;  // bewusst „kein Ort“
};

Faustregel:

  • optional = nicht vorhanden
  • null = bewusst leer
  • undefined = meistens: irgendwas ist schiefgelaufen

3) Interfaces und Types (Spoiler: Du wirst beide nutzen)

interface Asset {
id: string;
name: string;
status: "ok" | "defekt";
}

type AssetId = string;

Grobe Faustregel:

  • interface für Objektformen (besonders, wenn erweitert wird)
  • type für alles andere (Union, Tuple, Utility Types)

Funktionen: TypeScript macht Deine API endlich ehrlich

Typisierte Funktionen

function add(a: number, b: number): number {
return a + b;
}

Rückgabewerte, die wirklich was sagen

type Result<T> =
| { ok: true; value: T }
| { ok: false; error: string };

function parseIntSafe(s: string): Result<number> {
const n = Number.parseInt(s, 10);
return Number.isNaN(n)
? { ok: false, error: "Keine Zahl" }
: { ok: true, value: n };
}

Jetzt kann man Fehler nicht mehr „vergessen“.

Der große Gamechanger: Narrowing

TypeScript ist richtig gut darin, Typen im Code „enger“ zu machen.

function print(x: string | number) {
if (typeof x === "string") {
console.log(x.toUpperCase());
} else {
console.log(x.toFixed(2));
}
}

Das ist im echten Code Gold wert, wenn Daten mal nicht sauber sind.

„Bitte niemals any“ (oder: wie man sich selbst sabotiert)

let x: any = 5;
x.doSomethingStupid().andCrash();

any ist wie „Sicherheitsgurt abschneiden, weil er zwickt“.

Wenn Du wirklich „unbekannt“ meinst, nutze unknown:

let data: unknown = JSON.parse('{"a":1}');

if (typeof data === "object" && data !== null) {
// jetzt erst weiter prüfen
}

TypeScript + Java (Spring Boot): Dream-Team

Wenn Du ein Spring-Boot-Backend hast, ist TypeScript der perfekte Partner für:

  • saubere DTOs
  • stabile API-Contracts
  • Refactoring ohne Angst
  • Frontend, das nicht „zufällig“ kaputt geht

Beispiel: Java DTO (Record)

public record AssetDto(String id, String name, String status) {}

TypeScript DTO

export type AssetDto = {
id: string;
name: string;
status: "ok" | "defekt" | "unbekannt";
};

Jetzt kommt der Trick: TypeScript-Typen sind nur zur Compile-Zeit. Wenn das Backend Mist schickt, musst Du es zur Laufzeit prüfen.

Runtime-Validation: Weil APIs manchmal lügen (auch ohne böse Absicht)

Empfehlung: zod (oder ein ähnliches Schema-Tool)

npm i zod
import { z } from "zod";

const AssetDtoSchema = z.object({
id: z.string(),
name: z.string(),
status: z.enum(["ok", "defekt", "unbekannt"]),
});

type AssetDto = z.infer<typeof AssetDtoSchema>;

async function fetchAssets(): Promise<AssetDto[]> {
const res = await fetch("/api/assets");
const json = await res.json();
return z.array(AssetDtoSchema).parse(json);
}

Das ist der Unterschied zwischen: „Hoffen, dass es passt“ und „Wissen, dass es passt“.

API-Client, der Spaß macht (und nicht überall Copy/Paste)

Ein kleiner Fetch-Wrapper

type HttpError = { status: number; message: string };

async function getJson<T>(url: string): Promise<T> {
const res = await fetch(url, { headers: { "Accept": "application/json" } });

if (!res.ok) {
throw { status: res.status, message: await res.text() } as HttpError;
}
return res.json() as Promise<T>;
}

Nutzung:

const assets = await getJson<AssetDto[]>("/api/assets");

Bonus: Abbruch bei Zeitdruck (AbortController)

async function getJsonWithTimeout<T>(url: string, ms: number): Promise<T> {
const ctrl = new AbortController();
const t = setTimeout(() => ctrl.abort(), ms);

try {
const res = await fetch(url, { signal: ctrl.signal });
if (!res.ok) throw new Error(await res.text());
return res.json() as Promise<T>;
} finally {
clearTimeout(t);
}
}

Der „Ich will nie wieder ohne“-Moment: tsconfig

Wenn Du TypeScript ernst nimmst, stell es streng ein. Das ist wie Helm + Knieschoner: sieht uncool aus, aber Du bleibst ganz.

In tsconfig.json sind diese Optionen (sehr) empfehlenswert:

  • strict: true
  • noUncheckedIndexedAccess: true
  • exactOptionalPropertyTypes: true
  • useUnknownInCatchVariables: true

Das sind keine Schikanen. Das sind Bug-Finder.

TypeScript im Alltag: 7 kleine Regeln, die Dir Jahre sparen

  1. Vertraue nie Daten aus fetch() ohne Validierung.
  2. Nutze unknown statt any.
  3. Tippe Funktionsgrenzen (Inputs/Outputs), nicht jeden lokalen Wert.
  4. Benutze Union Types für Status/Zustände.
  5. Schreibe kleine, wiederverwendbare Utilities (Result, API-Wrapper).
  6. Lass den Editor arbeiten: Autocomplete ist Dein Co-Pilot.
  7. Wenn Du refactorst und es kompiliert: lächle. Das ist Lebenszeit.

Praxisübung: Mini-Frontend für ein Spring-Boot-Backend

Aufgabe:

  • Backend liefert GET /api/ping und GET /api/assets
  • Frontend zeigt Ping und Assetliste an

Du brauchst:

  • Vite + React + TS (siehe Setup)
  • API-Wrapper (oben)
  • DTO + Validation (zod)

Das ist eine perfekte „ich hab’s verstanden“-Übung.

Deutschsprachige Video-Tutorials / Lernmaterial

Gute deutschsprachige TypeScript-Ressourcen sind seltener als bei Java, aber diese hier sind brauchbar:

Wenn Du auch Englisch akzeptierst (leider oft die besten Kurse):

Schlusswort

TypeScript ist nicht „mehr Tipparbeit“. TypeScript ist „weniger Rumstochern“.

Es ist der Unterschied zwischen:

  • „Wird schon passen“ und
  • „Der Compiler hat’s geprüft“

Und in einem großen Projekt, das lange lebt, ist das nicht nur nett. Das ist ein Superpower-Upgrade.

Also: TypeScript an, strict mode an, Spaß haben.

Und wenn Du dabei Java im Backend hast: Keine Sorge. TypeScript gleicht das aus. 😉