Zum Inhalt springen

Programmierung & Entwicklung

JavaScript und TypeScript lernen

JavaScript und TypeScript lernen: praxisnaher Einstieg für Web, async, DOM, Node und sauberen Umstieg auf TypeScript, Schritt für Schritt.

16. Juli 2023 7 Min. Lesezeit

KI-generiert

JavaScript ist überall: im Browser, auf dem Server, in Build-Tools. Ohne JavaScript kommst du in der Webentwicklung nicht weit. Und ohne TypeScript wirst du bei wachsenden Projekten schnell den Überblick verlieren. Dieser Guide zeigt dir einen pragmatischen Weg vom ersten Script bis zur sauberen TypeScript-Codebasis.

JavaScript verstehen: Was du wirklich brauchst

Bevor du TypeScript anfasst, brauchst du ein solides JavaScript-Fundament. Du musst nicht jede Sprach-Ecke auswendig kennen, aber diese Bausteine sollten sitzen:

  • Datentypen: number, string, boolean, null, undefined, Arrays, Objekte, Funktionen
  • Variablen: let, const und warum var heute kaum noch sinnvoll ist
  • Funktionen: Funktionsdeklarationen, Funktionsausdrücke, Arrow Functions
  • Kontrollstrukturen: if, switch, for, while, for...of
  • Module: import und export mit ES-Modulen
  • Fehlerbehandlung: try, catch, finally, eigene Fehlerobjekte

Ein kleines Beispiel, das viele Grundlagen kombiniert:

// greetings.js
export function greetUser(name) {
 if (!name) {
 throw new Error('Name ist erforderlich');
 }

 const greeting = `Hallo ${name}, willkommen in der JS-Welt!`;
 return greeting;
}

// main.js
import { greetUser } from './greetings.js';

try {
 console.log(greetUser('Maria'));
} catch (error) {
 console.error('Fehler:', error.message);
}

Wenn du solche Beispiele ohne Stolpern lesen, erklären und anpassen kannst, bist du bereit für die nächsten Schritte.

Sync vs. Async: Warum JavaScript anders tickt

JavaScript läuft in der Regel in einem Single Thread. Statt viele Threads zu starten, arbeitet es mit einem Event Loop und asynchronem Code. Das ist entscheidend für:

  • HTTP-Requests im Browser
  • Dateizugriffe in Node.js
  • Datenbankabfragen in Backends
  • Timer und Benutzerinteraktionen

Die klassischen Werkzeuge dafür sind:

  • Callbacks
  • Promises
  • async und await

Callbacks sind historisch wichtig, aber für neue Projekte solltest du direkt mit Promises und async/await arbeiten.

Promises und async/await im Alltag

Ein typischer HTTP-Request im Browser:

async function loadUsers() {
 const response = await fetch('https://api.example.com/users');
 if (!response.ok) {
 throw new Error('Request fehlgeschlagen: ' + response.status);
 }
 const users = await response.json();
 return users;
}

loadUsers()
 .then(users => {
 console.log('Benutzer:', users);
 })
 .catch(error => {
 console.error('Fehler beim Laden:', error.message);
 });

Wichtige Punkte, die du verstehen solltest:

  • async macht aus einer Funktion eine Promise-funktion, egal ob du explizit ein Promise zurückgibst.
  • await pausiert die Funktion, bis das Promise erfüllt oder abgelehnt ist.
  • Fehler in async-Funktionen landen im zugehörigen catch des Promise-Chains oder im try/catch innerhalb der Funktion.

Ein typischer Stolperstein sind mehrere unabhängige Requests, die du unnötig nacheinander abarbeitest. Besser:

async function loadData() {
 const [users, posts] = await Promise.all([
 fetch('/api/users').then(r => r.json()),
 fetch('/api/posts').then(r => r.json())
 ]);

 return { users, posts };
}

Verstehe, wann paralleles Warten mit Promise.all sinnvoll ist und wann du bewusst sequenziell bleiben musst.

DOM im Browser: Interaktion mit der Seite

Im Browser ist der DOM dein Spielfeld. Du musst wissen, wie du:

  • Elemente auswählst: document.querySelector, querySelectorAll
  • Inhalte und Attribute änderst: textContent, innerHTML, setAttribute
  • Events behandelst: addEventListener
  • Formulare ausliest und validierst

Ein kleines, aber realistisches Beispiel:

const form = document.querySelector('#login-form');
const messageBox = document.querySelector('#message');

form.addEventListener('submit', event => {
 event.preventDefault();

 const email = form.elements.email.value;
 const password = form.elements.password.value;

 if (!email || !password) {
 messageBox.textContent = 'Bitte E-Mail und Passwort eingeben.';
 return;
 }

 messageBox.textContent = `Eingeloggt als ${email}`;
});

Wenn du mit solchen Snippets kleine UI-Funktionen in bestehende Webseiten einbauen kannst, bist du auf einem guten Weg.

Node.js: JavaScript außerhalb des Browsers

Mit Node.js läuft JavaScript auf dem Server oder auf deinem Rechner für Skripte und Tooling. Wichtige Unterschiede zum Browser:

  • Kein DOM, dafür Zugriff auf Dateisystem, Netzwerk, Prozesse
  • Andere globale Objekte: process, __dirname, require (CommonJS) oder ES-Module mit import
  • Oft andere APIs, zum Beispiel fs für Dateizugriffe

Ein einfaches Node-Script, das Daten aus einer JSON-Datei lädt:

import { readFile } from 'node:fs/promises';

async function loadConfig() {
 const raw = await readFile('./config.json', 'utf-8');
 return JSON.parse(raw);
}

loadConfig()
 .then(config => {
 console.log('Config geladen:', config);
 })
 .catch(error => {
 console.error('Fehler beim Laden der Config:', error.message);
 process.exit(1);
 });

Für Build-Skripte, kleine Helferlein im Projekt oder serverseitige APIs ist Node.js heute Standard. Gerade in Münchner Teams triffst du Node.js oft im DevOps-Umfeld und im Frontend-Buildprozess.

Warum TypeScript? Probleme, die du sonst früher oder später hast

Reines JavaScript ist flexibel, aber diese Flexibilität rächt sich in größeren Projekten. Typische Probleme:

  • Du vertippst dich im Property-Namen, der Fehler taucht erst zur Laufzeit auf.
  • Du änderst die Struktur eines Objekts, vergisst aber irgendwo eine Stelle.
  • Eine Funktion erwartet eine Zahl, bekommt aber einen String aus einem Formular.
  • Refactorings mit Suchen/Ersetzen sind riskant und unzuverlässig.

TypeScript setzt genau hier an. Es erweitert JavaScript um:

  • Typen für Variablen, Parameter und Rückgabewerte
  • Interfaces und Typ-Aliase
  • Enums und Literaltypen
  • Statische Analyse und bessere IDE-Unterstützung

Du schreibst weiterhin JavaScript, aber eben typisiert. Der Compiler warnt dich, bevor du den Code ausführst.

Beispiel ohne TypeScript:

function calculateDiscount(price, discountPercent) {
 return price - price * (discountPercent / 100);
}

const finalPrice = calculateDiscount('100', 10); // läuft, aber liefert Unsinn

Mit TypeScript:

function calculateDiscount(price: number, discountPercent: number): number {
 return price - price * (discountPercent / 100);
}

const finalPrice = calculateDiscount('100', 10);
// Fehler beim Kompilieren: Argument vom Typ "string" ist nicht dem Parameter vom Typ "number" zuweisbar.

Dieser Fehler wird gar nicht erst in dein Bundle eingebaut. Deine IDE markiert ihn sofort.

Zentrale TypeScript-Konzepte für den Einstieg

Du musst nicht alle TypeScript-Features kennen, um loszulegen. Diese Bausteine reichen für einen soliden Start:

Grundtypen und Typinferenz

TypeScript kann viel selbst ableiten:

let name = 'Anna'; // Typ: string
let age = 32; // Typ: number
let isAdmin = false; // Typ: boolean

Explizite Typen brauchst du vor allem an Schnittstellen:

function greet(name: string): string {
 return `Hallo ${name}`;
}

Objekttypen und Interfaces

Statt lose Objekte zu verwenden, definierst du eine Form:

interface User {
 id: number;
 name: string;
 email?: string; // optional
}

function printUser(user: User): void {
 console.log(`User #${user.id}: ${user.name}`);
}

Das hilft dir massiv bei API-Objekten, Formularen und Konfigurationen.

Union Types und Literaltypen

Gerade bei Statuswerten und Events sind Unions Gold wert:

type Status = 'idle' | 'loading' | 'success' | 'error';

let status: Status = 'idle';
status = 'loading';
// status = 'finished'; // Fehler, nicht im erlaubten Set

Generics im Alltag

Generics wirken abstrakt, sind aber im Alltag oft simpel:

function wrapInArray<T>(value: T): T[] {
 return [value];
}

const numbers = wrapInArray(5); // Typ: number[]
const names = wrapInArray('Maria'); // Typ: string[]

Du nutzt Generics ständig bei Arrays, Promises und vielen Bibliotheken, ohne sie immer explizit zu schreiben:

async function fetchUser(): Promise<User> {
 const response = await fetch('/api/user');
 return response.json();
}

Migrationsweg: Von JavaScript zu TypeScript ohne Big Bang

Der größte Fehler beim Umstieg ist der Versuch, ein komplettes Projekt auf einmal zu migrieren. Besser ist ein schrittweiser Ansatz.

1. TypeScript-Toolchain einführen

Zuerst richtest du TypeScript ein, ohne Code zu ändern:

  1. tsconfig.json anlegen, zum Beispiel mit npx tsc --init.
  2. Den Compiler so konfigurieren, dass er existierenden JS-Code akzeptiert:

Wichtige Optionen für den Einstieg:

  • "allowJs": true
  • "checkJs": true
  • "strict": false (kannst du später hochdrehen)
  • "noEmit": true für reinen Typcheck, wenn dein Build-Tool das Bundling übernimmt
  1. Build-Prozess anpassen, zum Beispiel via ts-loader in Webpack, esbuild oder die integrierte TS-Unterstützung in Tools wie Vite.

Jetzt bekommst du bereits Typfehler für deinen JavaScript-Code, wenn du JSDoc-Kommentare nutzt.

2. JSDoc-Typen in bestehendem JavaScript nutzen

Bevor du Dateien umbenennst, kannst du Typen mit JSDoc definieren:

/**
 * @typedef {Object} User
 * @property {number} id
 * @property {string} name
 * @property {string} [email]
 */

/**
 * @param {User} user
 */
function printUser(user) {
 console.log(user.name);
}

TypeScript versteht diese Kommentare und kann dir Hinweise geben, auch wenn die Dateien .js heißen.

3. Schrittweise auf .ts oder .tsx umstellen

Starte mit neuen Dateien direkt in TypeScript. Für existierenden Code:

  • Suche dir einen klar abgegrenzten Bereich, zum Beispiel ein Util-Verzeichnis.
  • Benenne eine Datei von .js auf .ts um.
  • Ergänze minimale Typen an öffentlichen Schnittstellen.
  • Löse die Typfehler, die der Compiler meldet.
  • Teste den Code.

Wichtige Zwischenlösung: any. Es ist besser, einzelne Stellen bewusst mit any zu markieren, als das gesamte Typensystem abzuschalten.

function parseConfig(raw: any): Config {
 // TODO: Typen genauer machen
 return {
 host: raw.host,
 port: Number(raw.port)
 };
}

So weißt du später, wo du nachschärfen musst.

4. Strikter werden, wenn die Basis steht

Wenn der Großteil deines Codes typisiert ist, kannst du nach und nach:

  • "strict": true aktivieren
  • "noImplicitAny": true setzen
  • "strictNullChecks": true aktivieren

Mach das nicht alles auf einmal, sondern schrittweise und mit Blick auf die Fehlermeldungen.

Praxisfokus: Wo du starten solltest

JavaScript und TypeScript sind riesige Themen. Für einen Einstieg mit Fokus auf Web- und Tooling-Aufgaben empfehle ich folgende Reihenfolge:

  1. Grundlagen JavaScript im Browser Kleine DOM-Manipulationen, Event-Handling, einfache Fetch-Requests.

  2. Asynchrones JavaScript verstehen Promises, async/await, Fehlerbehandlung, paralleles vs. sequenzielles Warten.

  3. Node.js als Werkzeugkasten Kleine CLI-Skripte, Dateien lesen und schreiben, einfache HTTP-Requests, Automatisierung im Projekt.

  4. TypeScript in neuen Modulen Neue Features direkt in .ts schreiben, einfache Typen, Interfaces, Unions.

  5. Schrittweise Migration alter Bereiche JSDoc-Typen, dann Umbenennen in .ts, dann striktere Typen.

Wenn du das strukturiert durchgehst und dir Zeit für eigene Mini-Projekte nimmst, bist du schnell an dem Punkt, an dem du produktiv und sicher mit JavaScript und TypeScript arbeiten kannst. Für tiefergehende Schulungen, gerade auch in Präsenz in München, findest du passende Angebote und aktuelle Termine bei cmt.de.

Nächster Schritt

Passenden Kurs zu Programmierung finden.

Feste Termine, erfahrene Trainer, Präsenz in München und Durchführungsgarantie. Buchen kannst du direkt auf cmt.de.