navigation Navigation


Inhaltsverzeichnis

Einführung


TypeScript erweitert JavaScript um statische Typisierung und objektorientierte Features, die den Entwicklungsprozess robuster und effizienter gestalten. Als Superset von JavaScript bietet TypeScript fortschrittliche Werkzeuge für Fehlerprüfung zur Kompilierungszeit, verbesserte IDE-Unterstützung und umfassende Dokumentationsmöglichkeiten. Diese Grundlagen-Einführung behandelt die wesentlichen Konzepte, Syntax und Vorteile der typsicheren Entwicklung mit TypeScript.

Inhaltsverzeichnis

    Entstehung und Philosophie

    TypeScript wurde 2012 von Microsoft veröffentlicht, um ein fundamentales Problem von JavaScript zu lösen: Das Fehlen eines statischen Typsystems. JavaScript wurde ursprünglich für kleine Scripte in Webseiten entwickelt, wird heute aber für komplexe Anwendungen mit Millionen von Codezeilen verwendet. Diese Evolution brachte Herausforderungen mit sich:

    • Laufzeitfehler: Typfehler werden erst entdeckt, wenn der Code ausgeführt wird.
    • Schlechte Tooling-Unterstützung: IDEs können ohne Typinformationen nur begrenzt helfen.
    • Schwierige Refactorings: Große Codeänderungen sind riskant ohne Compiler-Unterstützung.
    • Unklare APIs: Funktionssignaturen geben keine Hinweise auf erwartete Datentypen.

    TypeScript löst diese Probleme durch ein optionales, graduelles Typsystem.

    Das bedeutet:

    • Optional: Man kan TypeScript schrittweise einführen. Nicht jede Variable muss typisiert werden.
    • Graduell: Man kann mit JavaScript beginnen und nach und nach Typen hinzufügen.
    • Strukturell: TypeScript verwendet “Duck Typing” - wenn es wie eine Ente aussieht und quakt, ist es eine Ente.

    Funktionsweise

    TypeScript ist technisch gesehen ein Transpiler (Source-to-Source Compiler). Der TypeScript-Code wird in JavaScript kompiliert, das dann in jedem JavaScript-Runtime läuft.

    1. TypeScript Code (.ts)
    2. TypeScript Compiler (tsc)
    3. JavaScript Code (.js)
    4. JavaScript Runtime

    Wichtige Konzepte

    Compile-Time vs. Runtime

    • TypeScript existiert nur zur Compile-Zeit
    • Alle Typ-Informationen werden beim Kompilieren entfernt (“Type Erasure”)
    • Der resultierende JavaScript-Code enthält keine Typ-Informationen
    • Typen haben keinen Einfluß auf die Laufzeit-Performance

    Superset-Prinzip

    • Jeder gültige JavaScript-Code ist auch gültiger TypeScript-Code
    • TypeScript fügt JavaScript neue Syntax hinzu, ändert aber nicht die Semantik
    • Man kann .js Dateien einfach in .ts umbenennen als Startpunkt

    Strukturelle Typisierung

    Beispiel
    // TypeScript kümmert sich um die
    // Struktur, nicht um Namen
    interface Point {
        x: number;
        y: number;
    }
    
    // Diese Klasse implementiert Point
    // implizit durch ihre Struktur
    class Coordinate {
        constructor(
            public x: number,
            public y: number
        ) {}
    }
    
    function printPoint(p: Point) {
        console.log(`(${p.x}, ${p.y})`);
    }
    
    // Funktioniert, obwohl Coordinate Point
    // nicht explizit implementiert wurde
    printPoint(new Coordinate(10, 20));

    Vorteile von TypeScript

    Früherkennung von Fehlern

    In JavaScript werden viele Fehler erst zur Laufzeit entdeckt.

    Im folgenden JavaScript-Beispiel wird der Fehler erst bei der Ausführung wirklich sichtbar.

    Beispiel - JavaScript
    function calculatePrice(price, quantity) {
        return price * quantity;
    }
    
    calculatePrice("Art-Name", 3);

    Hier würde man als Ergebnis ein NaN erhalten. Allerdings erst, wenn man dieses Script ausführen würde.

    TypeScript erkennt solche Fehler während der Entwicklung. Hier wurden die Typen für die Parameter hinzugefügt. Somit weiß nun die IDE, dass price nur vom Typ number (Zahl) sein kann.

    Beispiel - TypeScript
    function calculatePrice(price: number, quantity: number) {
        return price * quantity;
    }
    
    calculatePrice("Art-Name", 3); // Das Argument vom Typ "string" kann dem Parameter vom Typ "number" nicht zugewiesen werden.

    Verbesserte IDE-Unterstützung

    TypeScript ermöglicht IDEs, intelligente Features anzubieten:

    • Autocompletion: Die IDE erkennt alle verfügbaren Properties und Methoden
    • Refactoring: Sichere Umbenennungen über die gesamte Codebase
    • Navigation: Man kann zur Definition springen und alle Referenzen finden
    • Inline-Dokumentation: Typ-Informationen als Dokumentation

    Selbstdokumentierender Code

    Typen fungieren als lebende Dokumentation.

    Beispiel - Ohne Typen
    function processUser(user, options) {
        // ...
    }

    Hier ist nicht klar, was diese Funktion erwartet.

    Mit Typen sind die Erwartungen klar.

    Beispiel - Mit Typen
    interface User {
        id: string;
        name: string;
        email: string;
    }
    
    interface ProcessOptions {
        sendEmail?: boolean;
        updateLastLogin?: boolean;
    }
    
    function processUser(user: User, options: ProcessOptions): void {
        // ...
    }

    Refactoring-Unterstützung

    Bei großen Codebasen ist Refactoring in JavaScript riskant. TypeScript macht es einfacher.

    Beispiel
    // Änderung einer Property
    interface Product {
        id: string;
        // name: string;
        title: string;
        price: number;
    }
    
    // TypeScript zeigt Fehler an allen Stellen,
    // die noch 'name' verwenden

    Installation und Setup

    TypeScript kann auf verschiedene Arten verwendet werden.

    Globale Installation

    Um TypeScript global auf dem System zu installieren, wird NodeJS und NPM benötigt.

    npm install -g typescript

    Projekt-Setup

    Neues Projekt mit TypeScript initialisieren.

    mkdir my_project
    cd my_project
    npm init -y

    Anschließend TypeScript als Entwicklungsabhängigkeit installieren. Da TypeScript zu JavaScript kompiliert wird, wird das Paket “TypeScript” nur während der Entwicklung benötigt.

    npm install --save-dev typescript

    TypeScript-Konfiguration erstellen

    npx tsc --init

    Grundlegende Konfiguration

    Die Konfiguration für TypeScript wird in der Regel in einer tsconfig.json festgehalten. Die tsconfig.json ist das Herzstück jedes TypeScript-Projekts.

    Diese Datei (Konfiguration) definiert:

    • Welche Dateien kompiliert werden sollen
    • Welche Compiler-Optionen verwendet werden
    • Wie strikt die Typ-Prüfung sein soll

    Hier eine grundlegende Beispiel-Konfiguration.

    tsconfig.json
    {
        "compilerOptions": {
            "target": "es2020",
            "module": "commonjs",
            "strict": true,
            "esModuleInterop": true,
            "skipLibCheck": true,
            "forceConsistentCasingInFileNames": true,
            "outDir": "./dist",
            "rootDir": "./src"
        },
        "include": ["src/**/*"],
        "exclude": ["node_modules", "dist"]
    }

    Die einzelnen Optionen werden in einem separaten Artikel erklärt.

    TypeScript vs. JavaScript - Praktischer Vergleich

    Betrachten wir ein realistisches Beispiel - eine Funktion zur Verarbeitung von Benutzerdaten.

    JavaScript-Version
    function processUserData(users, options) {
        if (!options.includeInactive) {
            users = users.filter(u => u.active);
        }
    
        return users.map(user => {
            id: user.id,
            displayName: options.useFullname
                ? `${user.firstName} ${user.lastName}`
                : user.firstName,
            email: user.email.toLowerCase(),
            lastLogin: new Date(user.lastLogin)
        });
    }
    TypeScript-Version
    interface User {
        id: string;
        firstName: string;
        lastName: string;
        email: string;
        active: boolean;
        lastLogin: string | Date;
    }
    
    interface ProcessOptions {
        includeInactive?: boolean;
        useFullName?: boolean;
    }
    
    interface ProcessedUser {
        id: string;
        displayName: string;
        email: string;
        lastLogin: Date;
    }
    
    function processUserData(users: User[], options: ProcessOptions): ProcessedUser[] {
        let filteredUsers = users;
    
        if (!options.includeInactive) {
            filteredUsers = users.filter(u => u.active);
        }
    
        return filteredUsers.map(user => {
            id: user.id,
            displayName: options.useFullName
                ? `${user.firstName} ${user.lastName}`
                : user.firstName,
            email: user.email.toLowerCase(),
            lastLogin: user.lastLogin instanceof Date
                ? user.lastLogin
                : new Date(user.lastLogin)
        });
    }

    Die TypeScript-Version bietet:

    • Klare Dokumentation der erwarteten Datenstrukturen
    • Compile-Zeit-Validierung aller Zugriffe
    • Autocompletion für alle Properties
    • Sichere Refactorings