🛠️Praxis

TypeScript im Alltag: Komponenten typisieren, Daten von APIs beschreiben, fremde Bibliotheken einbinden und bestehendes JavaScript Schritt für Schritt umstellen.

⚛️TypeScript mit React

Komponenten sind Funktionen – Props sind ihr erster Parameter. (Im Spielplatz mit stark vereinfachten React-Typen; in echten Projekten liefert sie @types/react.)

Props sind einfach ein Objekttyp für den ersten Parameter. Optionale Props bekommen ? und oft einen Standardwert in der Destrukturierung. Falsche oder fehlende Props meldet der Compiler direkt am JSX.

Komponente.tsx · bearbeitbar, Maus über Namen zeigt Typen
JavaScript (erzeugt von tsc) · …nur lesen

🔌Typen für APIs: aus JSON

Fügen Sie eine Beispielantwort ein – daraus entstehen Interfaces. Arrays mit Objekten werden zusammengeführt; was nicht in jedem Element vorkommt, wird optional.
Beispiel-JSON (z. B. Antwort von https://api.example.org/buecher/4711)
Erzeugte TypeScript-Typennur lesen

Achtung: Ein einzelnes Beispiel verrät nicht alles – Felder, die im Beispiel null sind, bekommen den Typnull. Die erzeugten Typen sind ein Startpunkt, keine Garantie für echte Serverantworten.

⚠️ Typen prüfen keine Daten
Die Annotation const daten: Buch = await antwort.json() ist ein Versprechen, keine Prüfung (json()liefert any). Für Daten von außen: als unknown annehmen und mit einem Type Guard oder einer Validierungsbibliothek prüfen.
✅ Eine Quelle der Wahrheit
Gibt es eine OpenAPI-Beschreibung, erzeugt man die Typen daraus (z. B. im Build), statt sie von Hand abzuschreiben. Ändert sich die API, meldet der Compiler alle betroffenen Stellen.

📜Typen für APIs: aus OpenAPI

openapi.json (Ausschnitt, erfundene Bücherei-API)
{
  "components": {
    "schemas": {
      "Status": {
        "type": "string",
        "enum": [
          "verfuegbar",
          "ausgeliehen",
          "vorgemerkt"
        ]
      },
      "Autor": {
        "type": "object",
        "required": [
          "id",
          "name"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "webseite": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          }
        }
      },
      "Buch": {
        "type": "object",
        "description": "Ein Buch im Bestand",
        "required": [
          "isbn",
          "titel",
          "status",
          "autoren"
        ],
        "properties": {
          "isbn": {
            "type": "string",
            "description": "ISBN-13"
          },
          "titel": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/Status"
          },
          "autoren": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Autor"
            }
          },
          "erschienen": {
            "type": "string",
            "format": "date"
          },
          "seiten": {
            "type": "integer"
          },
          "schlagworte": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "BuchListe": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Buch"
        }
      }
    }
  }
}
generierte Typen (vereinfachter Generator dieser App)
  • required → Pflichtfeld, sonst ?
  • enum → Union von Literalen
  • "type": ["string", "null"] (OpenAPI 3.1) bzw. nullable (3.0) → | null
  • $ref → Verweis auf den benannten Typ
  • integer → number (TypeScript kennt keinen Ganzzahltyp)

📘.d.ts und DefinitelyTyped

Eine .d.ts-Datei enthält nur Typen, keinen Code. tsc erzeugt sie mit declaration: true – links der Code, rechts die daraus erzeugte Deklaration.
TypeScript · bearbeitbar, Maus über Namen zeigt Typen
.d.ts (Typdeklarationen)nur lesen

Woher kommen Typen für Pakete?

  1. Im Paket selbst (Feld types bzw. exports in der package.json) – der Normalfall bei modernen Paketen.
  2. DefinitelyTyped: ein Community-Repository mit Typen für Pakete ohne eigene Typen, installierbar als npm i -D @types/paketname.
  3. Selbst geschrieben: eine eigene .d.ts mit declare module (rechts).
⚠️ TypeScript 6.0: types ist leer
Globale Typen wie @types/node werden seit 6.0 nicht mehr automatisch geladen – in der tsconfig.jsongehört "types": ["node"] eingetragen (sonst Fehler TS2591 bei process).
// Eigene Deklaration für ein JavaScript-Paket ohne Typen (z. B. types/rechner-lib.d.ts)
declare module "rechner-lib" {
  export function addiere(a: number, b: number): number;
  export const version: string;
}

🚚Migration von JavaScript nach TypeScript

Nicht alles auf einmal: umbenennen, locker starten, dann Schritt für Schritt strenger werden. Die Fehlerzahlen kommen vom Compiler.
Schritt 1 / 4 · Tasten ← →
warenkorb.ts · strict: falsenur lesen

1 · Umbenennen, locker starten

Die .js-Datei wird zu .ts – mit strict: false. Gültiges JavaScript ist (fast immer) gültiges TypeScript: 0 Fehler, aber schon jetzt leitet der Compiler Typen ab (warenkorb ist ein Array von Objekten mit name und preis) und die Autovervollständigung funktioniert. In gemischten Projekten erlaubt allowJs, dass .js- und .ts-Dateien nebeneinander leben.

💡 Große Projekte
Mit allowJs leben .js- und .ts-Dateien nebeneinander; mitcheckJs prüft der Compiler sogar JavaScript-Dateien (Typen per JSDoc-Kommentar wie /** @param {number} x */). So lässt sich Datei für Datei umstellen.

🕳️Häufige Fehler und Stolperfallen

Die gefährlichsten Fehler sind die, die der Compiler nicht meldet. Links das Problem, rechts die bessere Variante – jeweils mit Fehlerzahl vom Compiler.

as statt Prüfung

as behauptet nur. Kommt vom Server etwas anderes, knallt es erst zur Laufzeit – der Compiler meldet nichts.

😬 so nicht…
👍 besser…

any ist ansteckend

Was aus any kommt, ist wieder any – Tippfehler rutschen durch. JSON.parse liefert any!

😬 so nicht…
👍 besser…

Indexzugriff ohne undefined

Standardmäßig ist liste[10] vom Typ des Elements – obwohl das Element fehlen kann.

😬 so nicht…
👍 besser (mit noUncheckedIndexedAccess)…

Object.keys liefert string[]

Wegen der strukturellen Typisierung kann ein Objekt mehr Schlüssel haben als sein Typ – deshalb ist Object.keys bewusst nur string[].

😬 so nicht…
👍 besser…

{} heißt nicht „leeres Objekt“

{} bedeutet „irgendein Wert außer null/undefined“ – auch Zahlen und Strings passen. Für „beliebiges Objekt“ nimmt man object oder Record<string, unknown>.

😬 so nicht…
👍 besser…

Numerische Enums

Numerische Enums sind lax: Werte werden als Zahlen durchgereicht und im Log steht 1 statt eines lesbaren Namens. Unions von String-Literalen sind lesbarer und löschbar.

😬 so nicht…
👍 besser…