1. JavaScript und TypeScript
JavaScript und TypeScript
Abschnitt betitelt „JavaScript und TypeScript“Die Sprachen dieses Schuljahres sind JavaScript und sein typisierter Aufsatz TypeScript. Der Grund ist das Jahresthema: Client-Server-Anwendungen im Web. JavaScript ist die einzige Sprache, die im Browser läuft, und mit Node.js läuft sie ebenso auf dem Server; Client und Server lassen sich damit in einer Sprache entwickeln, und genau das nutzt das Framework Next.js, mit dem wir ab der Jahresmitte arbeiten.
Dieses Kapitel legt die Basis: Werkzeuge, die Grundkonstrukte der Sprache im Schnelldurchlauf und die modernen Sprachmittel, die im Rest des Jahres ständig gebraucht werden. Wer die Programmierkonzepte (Variablen, Verzweigungen, Schleifen, Funktionen, Datenstrukturen) bereits aus einer anderen Sprache beherrscht, liest den ersten Teil als kompakte Wiederholung in neuer Syntax. Danach kommt mit TypeScript etwas wirklich Neues: statische Typen als Werkzeug gegen eine ganze Fehlerklasse.
Werkzeuge einrichten
Abschnitt betitelt „Werkzeuge einrichten“-
Node.js installieren. Laden Sie die aktuelle LTS-Version von nodejs.org herunter (unter Linux über den Paketmanager). Node.js ist die JavaScript-Laufzeitumgebung außerhalb des Browsers; der Paketmanager npm wird mitinstalliert.
-
Installation prüfen.
Terminal-Fenster node --version # v22.x or newernpm --version -
Erstes Skript ausführen. Legen Sie eine Datei
hello.jsan und starten Sie sie im Terminal:const name = "Ada";console.log(`Hello, ${name}!`);Terminal-Fenster node hello.js -
VS Code: JavaScript- und TypeScript-Unterstützung ist eingebaut, es braucht keine Erweiterung. Empfehlenswert ist Prettier für einheitliche Formatierung.
console.log() schreibt ins Terminal (im Browser: in die Konsole der DevTools). Eine Tastatureingabe im Konsolenstil spielt in diesem Jahr kaum eine Rolle; Eingaben kommen aus Web-Formularen und HTTP-Anfragen.
Node bringt auch eine REPL mit: node ohne Dateinamen startet sie, Ausdrücke werden sofort ausgewertet, Strg+D beendet. Wie in jeder Sprache gilt: Sprachdetails klärt man am schnellsten durch Ausprobieren in der REPL.
JavaScript-Grundkonstrukte im Schnelldurchlauf
Abschnitt betitelt „JavaScript-Grundkonstrukte im Schnelldurchlauf“Die folgende Übersicht ist bewusst dicht; sie dient als Referenz und Wiederholung. Jedes Konstrukt gehört einmal selbst getippt und ausgeführt (Aufgabe 01).
Variablen und Typen
Abschnitt betitelt „Variablen und Typen“let attempts = 0; // variable, reassignableconst MAX_ATTEMPTS = 3; // constant, cannot be reassignedconst name = "Ada"; // const is the default; let only when needed
typeof 42 // "number" (no int/float distinction!)typeof "text" // "string"typeof true // "boolean"typeof undefined // "undefined"Auffälligkeiten: Es gibt nur einen Zahlentyp number (Gleitkomma; 7 / 2 ist 3.5, Ganzzahldivision schreibt sich Math.floor(7 / 2)). Neben null (“bewusst leer”) existiert undefined (“nie befüllt”). Als Standard gilt const; let nur, wenn die Variable tatsächlich neu zugewiesen wird. var ist Alt-Syntax und tabu.
Vergleiche und Wahrheitswerte
Abschnitt betitelt „Vergleiche und Wahrheitswerte“1 === 1 // true -> always use === and !==1 == "1" // true?! -> == converts types; never use it"" || "default" // "default"value ?? "default" // "default" only if value is null/undefinedJavaScript wandelt bei == Typen still um; die Regeln dafür sind schwer zu merken und eine häufige Fehlerquelle. Deshalb die eiserne Regel: immer === und !==. Der Operator ?? (Nullish Coalescing) liefert den Ersatzwert nur bei null/undefined, während || schon bei 0 oder "" zuschlägt; für Standardwerte ist ?? fast immer der richtige.
Kontrollstrukturen und Funktionen
Abschnitt betitelt „Kontrollstrukturen und Funktionen“if (score >= 90) { grade = 1;} else if (score >= 75) { grade = 2;} else { grade = 5;}
for (const color of colors) { // iterate over elements console.log(color);}
while (attempts < MAX_ATTEMPTS) { attempts += 1; }
function rectangleArea(width, height) { return width * height;}Blöcke werden mit geschweiften Klammern gebildet, die Einrückung ist Konvention (Code Linter erledigt sie). Namen schreiben sich in camelCase, Klassen in PascalCase, Konstanten in UPPER_CASE.
Funktionen und Arrow Functions
Abschnitt betitelt „Funktionen und Arrow Functions“JavaScript kennt zwei Schreibweisen für Funktionen. Beide sind in Gebrauch, und die Wahl zwischen ihnen ist keine reine Geschmacksfrage - an einer Stelle verhalten sie sich unterschiedlich, und das kostet regelmäßig Zeit bei der Fehlersuche.
// 1. function declarationfunction rectangleArea(width, height) { return width * height;}
// 2. arrow function, assigned to a constantconst double = (x) => x * 2; // implicit return, no bracesconst half = (x) => { return x / 2; }; // with braces: return is mandatoryconst greet = () => console.log("hi"); // no parameter: empty parenthesesconst makePoint = (x, y) => ({ x, y }); // returning an object literal needs ( )Drei Unterschiede sind der Rede wert:
- Kurzform: Ohne geschweifte Klammern gibt eine Arrow Function ihren Ausdruck automatisch zurück. Das macht sie als Argument für
map,filteroderreducelesbar - dort steht oft nur ein einziger Ausdruck. - Verfügbarkeit im Code: Eine
function-Deklaration ist in ihrem gesamten Gültigkeitsbereich verwendbar, auch oberhalb ihrer Definition (hoisting). Eine Arrow Function ist eine Zuweisung an eine Konstante und existiert erst ab der Zeile, in der sie steht. - Bedeutung von
this: Das ist der eigentliche Grund für die Unterscheidung, siehe unten.
Der Unterschied bei this
Abschnitt betitelt „Der Unterschied bei this“Eine function bekommt ihr this beim Aufruf zugewiesen - abhängig davon, wie sie gerufen wird. Eine Arrow Function hat kein eigenes this; sie übernimmt das ihrer Umgebung, festgelegt beim Schreiben.
const timer = { label: "render", seconds: 0,
// method: called as timer.report(), so this === timer report() { console.log(`${this.label}: ${this.seconds}s`); },
startBroken() { setInterval(function () { this.seconds += 1; // this is NOT timer here - silently does nothing }, 1000); },
startWorking() { setInterval(() => { this.seconds += 1; // arrow keeps the surrounding this: timer }, 1000); },};startBroken wirft keinen Fehler und zählt trotzdem nichts - der Klassiker unter den this-Fehlern.
Wann was
Abschnitt betitelt „Wann was“| Situation | Wahl | Grund |
|---|---|---|
| Benannte Funktion auf Modulebene | function | gut lesbar, per hoisting überall im Modul verfügbar, im Stacktrace mit Namen |
Argument für map/filter/reduce und andere Rückruffunktionen | Arrow | kurz, meist ein einziger Ausdruck |
Rückruffunktion, die auf das umgebende this zugreift (Timer, Event-Handler in Klassen) | Arrow | übernimmt this der Umgebung |
| Methode in einem Objektliteral | function beziehungsweise Kurzform report() { } | braucht ihr eigenes this auf das Objekt |
| Konstruktorfunktion, Generator | function | Arrow Functions können das nicht |
Als Faustregel für dieses Jahr: Auf Modulebene function, als Argument Arrow. In React-Komponenten (ab Kapitel 7) sind Ereignisbehandler durchgängig Arrow Functions, und Sie werden sehen, warum das dort die einzige praktikable Wahl ist.
Arrays und ihre Methoden
Abschnitt betitelt „Arrays und ihre Methoden“const durations = [240, 180, 320];durations.push(150); // appenddurations[0]; // 240durations.length; // 4durations.includes(320); // true
// the big three: transform, filter, aggregateconst doubled = durations.map((d) => d * 2);const long = durations.filter((d) => d >= 180);const total = durations.reduce((sum, d) => sum + d, 0);map, filter und reduce nehmen Funktionen als Argumente und ersetzen einen Großteil der klassischen Schleifen. Sie sind der Alltagsstil in JavaScript-Projekten und tauchen ab dem React-Kapitel in praktisch jedem Codebeispiel auf; investieren Sie hier Übungszeit.
Objekte
Abschnitt betitelt „Objekte“const clip = { filename: "intro.mp4", durationSeconds: 90, tags: ["opener", "draft"],};
clip.filename; // dot accessclip["filename"]; // bracket accessclip.fps = 25; // add a propertyDas Objektliteral ist der universelle Datencontainer von JavaScript, vergleichbar mit Dictionaries oder Maps anderer Sprachen, nur mit Punktzugriff. JSON, das Datenformat des ganzen Jahres, ist nichts anderes als diese Syntax in Textform.
Moderne Sprachmittel
Abschnitt betitelt „Moderne Sprachmittel“Vier Sprachmittel, ohne die kein aktuelles JavaScript-Projekt auskommt:
Destructuring
Abschnitt betitelt „Destructuring“Destructuring holt Werte aus einem Objekt oder Array heraus und legt sie in eigene Variablen.
// without destructuringconst filename = clip.filename;const durationSeconds = clip.durationSeconds;
// with destructuring - same result, one lineconst { filename, durationSeconds } = clip;Die Variablennamen entsprechen dabei den Feldnamen; die Reihenfolge in den geschweiften Klammern spielt keine Rolle. Bei Arrays ist es umgekehrt: Dort entscheidet die Position, und die Namen sind frei wählbar.
const [first, second] = durations; // by positionconst [, , third] = durations; // skip entries with commasVier Varianten, die im Jahresverlauf ständig vorkommen:
// 1. rename while unpacking (the field is called filename, the variable name)const { filename: name } = clip;console.log(name); // "intro.mp4"
// 2. default value, applied when the field is undefinedconst { fps = 25 } = clip;console.log(fps); // 25
// 3. unpack nested structuresconst { metadata: { codec } } = clip;console.log(codec); // "h264"
// 4. rest: collect everything not named above into one objectconst { filename, ...rest } = clip; // rest has all remaining fieldsconsole.log(rest); // { durationSeconds: 90, fps: 25, tags: ["opener", "draft"] }Besonders häufig steht Destructuring direkt in der Parameterliste. Die Funktion nimmt dann weiterhin ein Objekt entgegen, benennt aber in der Signatur, welche Felder sie tatsächlich braucht:
function describe({ filename, fps = 25 }) { return `${filename} @ ${fps} fps`;}
describe(clip); // called with the whole objectDas hat zwei praktische Folgen: Der Aufrufer muss sich keine Parameterreihenfolge merken, und die Signatur dokumentiert die Abhängigkeiten der Funktion. Genau in dieser Form tauchen ab dem React-Kapitel die Props jeder Komponente auf.
Spread und Rest
Abschnitt betitelt „Spread und Rest“Beide schreiben sich ..., arbeiten aber in entgegengesetzte Richtungen: Spread packt aus, Rest sammelt ein. Welches von beiden gemeint ist, verrät die Position - links von einem = oder in einer Parameterliste sammelt es, sonst packt es aus.
Spread schreibt den Inhalt eines Objekts oder Arrays in ein neues hinein:
const copy = { ...clip }; // shallow copyconst changed = { ...clip, fps: 50 }; // copy with one field overriddenconst merged = { ...defaults, ...userSettings }; // later wins on conflict
const all = [...listA, ...listB]; // concatenateconst withExtra = [...durations, 150]; // append without touching the originalconst maximum = Math.max(...durations); // spread as function argumentsZwei Punkte, die man dabei wissen muss. Erstens gilt bei Objekten: Bei gleichem Feldnamen gewinnt der spätere Eintrag - deshalb steht fps: 50 hinter dem Spread und nicht davor. Zweitens ist die Kopie flach (shallow): Verschachtelte Objekte und Arrays werden nicht mitkopiert, sondern weiterhin geteilt.
const copy = { ...clip };copy.filename = "outro.mp4"; // fine: the original keeps its filenamecopy.tags.push("final"); // careful: clip.tags changed too - same arrayFür verschachtelte Daten wird deshalb auf jeder betroffenen Ebene gespreadet ({ ...clip, tags: [...clip.tags, "final"] }) oder gleich structuredClone(clip) verwendet.
Rest ist die Gegenrichtung: Er sammelt, was übrig bleibt.
function sum(...values) { // collect all arguments into an array return values.reduce((total, v) => total + v, 0);}
sum(1, 2, 3); // values is [1, 2, 3]
function logRequest(method, ...details) { } // rest must come lastDas Muster { ...object, field: newValue } - Kopie mit geänderten Feldern statt Änderung am Original - ist im React-Kapitel die Standardtechnik für Zustandsänderungen. Der Grund dafür ist dort ein handfester: React erkennt Änderungen daran, dass ein neues Objekt vorliegt; eine Änderung am bestehenden bleibt unbemerkt. Es lohnt sich, das Muster jetzt schon zu verinnerlichen.
Optional Chaining
Abschnitt betitelt „Optional Chaining“const city = order?.customer?.address?.city; // undefined instead of crashconst fps = clip.metadata?.fps ?? 25; // combined with ???. bricht die Kette ohne Fehler ab, sobald ein Glied null/undefined ist. Zusammen mit ?? ergibt das den robusten Zugriff auf verschachtelte, möglicherweise unvollständige Daten, wie sie von fremden APIs kommen.
export function framesToTimecode(frames, fps = 25) { /* ... */ }export const DEFAULT_FPS = 25;
// main.jsimport { framesToTimecode, DEFAULT_FPS } from "./timecode.js";Jede Datei ist ein Modul; exportiert wird explizit, importiert namentlich. Damit Node die import-Syntax akzeptiert, gehört in die package.json des Projekts der Eintrag "type": "module" (oder die Datei heißt .mjs). Was es mit dieser package.json auf sich hat, behandelt das nächste Kapitel.
TypeScript: Typen als Werkzeug
Abschnitt betitelt „TypeScript: Typen als Werkzeug“JavaScript prüft Typen erst zur Laufzeit, und ist dann (sehr, sehr) tolerant:
function totalDuration(clips) { return clips.reduce((sum, clip) => sum + clip.durationSeconds, 0);}
totalDuration([{ filename: "intro.mp4", durationSecond: 90 }]); // NaNDer Tippfehler durationSecond (ohne s) fällt nicht auf: Der Zugriff liefert undefined, die Addition NaN, und das Programm läuft mit diesem Unsinn weiter. Solche Fehler kosten in wachsenden Projekten die meiste Debugging-Zeit, und genau gegen sie ist TypeScript gebaut: JavaScript plus Typangaben, geprüft vor dem Programmlauf vom Compiler tsc. TypeScript wird zu gewöhnlichem JavaScript übersetzt; im Browser und in Node läuft am Ende immer JavaScript.
Typannotationen und Inferenz
Abschnitt betitelt „Typannotationen und Inferenz“function totalDuration(clips: Clip[]): number { return clips.reduce((sum, clip) => sum + clip.durationSeconds, 0);}
let attempts: number = 0; // explicitlet name = "Ada"; // inferred as string; annotation unnecessaryDie Syntax folgt dem Muster wert: Typ, der Rückgabetyp steht nach der Parameterliste. TypeScript inferiert Typen, wo es sie sehen kann; annotiert werden vor allem Funktionsparameter und Datenstrukturen, nicht jede lokale Variable.
Interfaces: die Form der Daten
Abschnitt betitelt „Interfaces: die Form der Daten“interface Clip { filename: string; durationSeconds: number; tags: string[]; fps?: number; // optional property}
const intro: Clip = { filename: "intro.mp4", durationSeconds: 90, tags: ["opener"],};Ein interface beschreibt die Form eines Objekts: welche Felder, welche Typen, was optional ist (?). Der Tippfehler von oben ist damit ein Compile-Fehler mit präziser Meldung (Property 'durationSeconds' is missing), noch bevor das Programm startet. Interfaces sind zugleich Dokumentation: Wer Clip liest, kennt den Datenvertrag, ohne Code zu lesen. Genau in dieser Rolle begleiten sie das ganze Jahr, als Verträge für API-Daten.
Union Types und Literale
Abschnitt betitelt „Union Types und Literale“type RenderState = "queued" | "running" | "done" | "failed";
function label(state: RenderState): string { if (state === "done") return "finished"; return state;}
label("dne"); // compile error: not assignable to RenderStateEin Union Type zählt die erlaubten Möglichkeiten auf. Der Klassiker sind Zustands- und Statusfelder: Statt beliebiger Strings gibt es vier gültige Werte, und jeder Tippfehler fliegt beim Kompilieren auf. Auch Kombinationen wie string | null (“String oder bewusst leer”) sind Union Types; TypeScript zwingt dann an jeder Verwendungsstelle zur Behandlung des null-Falls.
Generics in Grundzügen
Abschnitt betitelt „Generics in Grundzügen“function firstOrNull<T>(items: T[]): T | null { return items.length > 0 ? items[0] : null;}
const clip = firstOrNull(clips); // type: Clip | nullconst tag = firstOrNull(["a", "b"]); // type: string | nullEin Generic ist ein Typ-Platzhalter: firstOrNull funktioniert für jede Elementart, und TypeScript rechnet den konkreten Typ am Aufrufort aus. In diesem Jahr lesen Sie Generics vor allem (Array<Clip>, ab dem nächsten Kapitel Promise<Response>); selbst schreiben werden Sie sie selten. Es genügt zu verstehen: Der Platzhalter in spitzen Klammern wird beim Verwenden durch einen echten Typ ersetzt.
TypeScript ausführen
Abschnitt betitelt „TypeScript ausführen“npm install --save-dev typescriptnpx tsc --init # creates tsconfig.jsonnpx tsc # type-check and compile to JavaScriptnpx tsx timecode.ts # run directly during development (tsx package)Im Alltag dieses Jahres übernimmt Next.js die komplette TypeScript-Werkzeugkette unsichtbar; tsc von Hand zu bedienen gehört trotzdem einmal erlebt, damit klar ist, was das Framework automatisiert. In der tsconfig.json bleibt die Voreinstellung "strict": true unangetastet: Der strenge Modus findet die meisten Fehler, und sich später an Strenge zu gewöhnen ist erfahrungsgemäß viel schwerer.
Ein vollständiges Beispiel
Abschnitt betitelt „Ein vollständiges Beispiel“Ein kleines typisiertes Werkzeug, das die Bausteine kombiniert: Aus einer Liste von Videoclips soll die Gesamtdauer je Tag berechnet werden.
interface Clip { filename: string; durationSeconds: number; tags: string[];}
function durationByTag(clips: Clip[]): Map<string, number> { const totals = new Map<string, number>(); for (const clip of clips) { for (const tag of clip.tags) { totals.set(tag, (totals.get(tag) ?? 0) + clip.durationSeconds); } } return totals;}
const clips: Clip[] = [ { filename: "intro.mp4", durationSeconds: 90, tags: ["opener"] }, { filename: "interview.mp4", durationSeconds: 845, tags: ["main", "voice"] }, { filename: "outro.mp4", durationSeconds: 45, tags: ["opener"] },];
for (const [tag, seconds] of durationByTag(clips)) { console.log(`${tag}: ${seconds} s`);}Beachtenswert: das Interface als Datenvertrag, Map<string, number> als Generic in Verwendung, ?? 0 für den ersten Treffer eines Tags und Destructuring in der Ausgabeschleife. Jeder Tippfehler in einem Feldnamen wäre ein Compile-Fehler.
any und unknown: die Notausgänge des Typsystems
Abschnitt betitelt „any und unknown: die Notausgänge des Typsystems“Zum Schluss zwei Typen, die auf den ersten Blick dasselbe bedeuten - “der Typ steht nicht fest” - und die sich in der Praxis so unterschiedlich verhalten, dass an ihnen die Qualität eines TypeScript-Projekts hängt.
any: die Prüfung abschalten
Abschnitt betitelt „any: die Prüfung abschalten“any bedeutet nicht “irgendein Typ”, sondern “hör auf zu prüfen”. Auf einem any-Wert ist jeder Zugriff, jeder Aufruf und jede Zuweisung erlaubt:
const value: any = "not a number";
const total: number = value; // accepted - and wrongconsole.log(value.toFixed(2)); // accepted - crashes at runtimeconsole.log(value.whatever.x); // accepted - crashes at runtimeDer Compiler schweigt zu allen drei Zeilen. Damit ist genau die Fehlerklasse zurück, gegen die TypeScript angetreten ist - und zwar unsichtbar, denn im Code steht ja ein Typ.
Verschärfend kommt hinzu: any ist ansteckend. Der Rückgabewert einer Funktion, die mit any arbeitet, ist wieder any, und so wandert der blinde Fleck durch das Programm. Ein einziges any an der falschen Stelle kann einen ganzen Modulpfad ungeprüft lassen.
unknown: der strenge Zwilling
Abschnitt betitelt „unknown: der strenge Zwilling“unknown sagt dasselbe über den Wert aus, zieht aber die umgekehrte Konsequenz: Solange nicht geklärt ist, was der Wert ist, ist nichts damit erlaubt.
const raw: unknown = JSON.parse(text);
const name = raw.filename; // error TS18046: 'raw' is of type 'unknown'Das ist keine Schikane, sondern die korrekte Aussage: Woher soll der Compiler wissen, dass in diesem JSON ein Feld filename steht? Der Weg zum Wert führt über eine Prüfung, und nach der Prüfung weiß auch der Compiler Bescheid (narrowing):
if (typeof raw === "object" && raw !== null && "filename" in raw) { console.log((raw as { filename: string }).filename);}Lesbarer wird das mit einer eigenen Prüffunktion, deren Rückgabetyp value is Clip dem Compiler das Ergebnis der Prüfung mitteilt (type predicate):
function isClip(value: unknown): value is Clip { return typeof value === "object" && value !== null && "filename" in value && typeof (value as Clip).filename === "string";}
if (isClip(raw)) { console.log(raw.filename.toUpperCase()); // raw is a Clip in here}Der Unterschied in einem Satz
Abschnitt betitelt „Der Unterschied in einem Satz“any | unknown | |
|---|---|---|
| Aussage | ”prüf nicht mehr" | "noch nicht geklärt” |
| Zugriff ohne Prüfung | erlaubt | Compile-Fehler |
| Zuweisung an andere Typen | überallhin | nur nach Prüfung |
| Wirkung auf den Rest | ansteckend | eingesperrt |
any verlagert das Problem in die Laufzeit, unknown erzwingt seine Lösung beim Schreiben. Deshalb gilt: An jeder Stelle, an der man any schreiben möchte, ist unknown die bessere erste Wahl.
Wo Ihnen die beiden begegnen
Abschnitt betitelt „Wo Ihnen die beiden begegnen“Man sucht sich unknown selten aus - es kommt an den Rändern des Programms von selbst:
JSON.parse()liefert per Definition Daten unbekannter Form. Was ein Server schickt, weiß der Compiler nicht.catch (error)ist unter"strict": truevom Typunknown, denn in JavaScript kann alles geworfen werden, nicht nurError. Daher der Pflichtgriffif (error instanceof Error), bevorerror.messagegelesen wird.- Fremde Pakete ohne Typinformationen liefern
any; hier lohnt der Blick, ob es ein Paket@types/...gibt. - Formulardaten und HTTP-Anfragen kommen von außen und sind damit grundsätzlich unbekannt.
Genau hier liegt die Brücke zum Rest des Jahres: Die Frage “wie wird aus unbekannten Daten ein geprüfter, typisierter Wert?” beantwortet Kapitel 12 mit Schema-Validierung - ein Werkzeug, das die Prüfung schreibt und den passenden Typ gleich mitliefert.
Utility-Types: Typen aus Typen bauen
Abschnitt betitelt „Utility-Types: Typen aus Typen bauen“Bisher haben wir Typen von Hand geschrieben. TypeScript kann Typen aber auch aus anderen Typen berechnen. Die dafür mitgelieferten Bausteine heißen Utility-Types: eingebaute Generics, die einen bestehenden Typ nehmen und einen abgewandelten zurückgeben. Der Vorteil ist immer derselbe - es gibt eine Quelle der Wahrheit, und die Abwandlungen bleiben automatisch synchron.
Als Ausgangspunkt dient ein Datenvertrag, wie er uns im Jahr ständig begegnen wird:
interface Item { id: number; name: string; price: number; // in cents available: number;}Partial<T> macht alle Felder optional - passend für ein Update, das nur einzelne Felder ändert:
// { id?: number; name?: string; price?: number; available?: number }function updateItem(id: number, changes: Partial<Item>): void { /* ... */ }
updateItem(1, { price: 350 }); // only the price, the rest stays untouchedOmit<T, K> entfernt Felder, Pick<T, K> behält nur die genannten. Ein neues Item hat noch keine id - die vergibt der Server:
type NewItem = Omit<Item, "id">; // everything except idtype ItemPreview = Pick<Item, "id" | "name">; // only id and nameReadonly<T> friert alle Felder ein; eine spätere Zuweisung ist dann schon zur Compile-Zeit ein Fehler:
const frozen: Readonly<Item> = { id: 1, name: "Cola", price: 200, available: 5 };frozen.price = 250; // error: cannot assign to 'price' because it is read-onlyRecord<K, V> beschreibt ein Objekt, das Schlüssel auf Werte abbildet - nützlich für Nachschlagetabellen:
// a lookup from item id to its ordered quantityconst quantities: Record<number, number> = { 1: 2, 4: 1 };Diese Bausteine lassen sich kombinieren (Partial<Omit<Item, "id">> ist “alle Felder außer id, und die optional”) - genau diese Kombination werden wir im Kapitel Mehrschichtarchitektur für Repository-Updates wiedersehen. Das folgende Video führt die wichtigsten Utility-Types an Beispielen vor:
Die Regeln fürs Jahr
Abschnitt betitelt „Die Regeln fürs Jahr“-
Standard ist
unknown, wenn der Typ tatsächlich offen ist - besonders an den Grenzen zu fremden Daten. -
Prüfen statt behaupten: aus
unknownwird ein brauchbarer Typ durchtypeof,instanceof,inoder eine eigene Prüffunktion. -
anynur als bewusste Notlösung, und dann mit einem Kommentar, der begründet, warum hier nicht geprüft wird. Ein unkommentiertesanyist im Review eine Anmerkung wert. -
Nie
anygegen eine Fehlermeldung einsetzen. Der Compiler meldet an dieser Stelle etwas Richtiges;anylöscht die Meldung, nicht das Problem.
Häufige Stolperfallen
Abschnitt betitelt „Häufige Stolperfallen“==statt===: stille Typumwandlung mit absurden Ergebnissen; immer===/!==.undefinedstatt Fehler: Der Zugriff auf ein nicht existierendes Feld liefertundefinedund knallt erst später. In TypeScript wird daraus ein Compile-Fehler; einer der Hauptgründe für die Sprache.constverhindert Neuzuweisung, nicht Veränderung:const list = []erlaubtlist.push(1), aber nichtlist = [1].- Nur ein Zahlentyp:
0.1 + 0.2 === 0.3istfalse(Gleitkomma); Geldbeträge rechnet man in Cent. - Falsches
thisin einer Rückruffunktion: Eine alsfunctiongeschriebene Rückruffunktion bringt ihr eigenesthismit und greift damit ins Leere - ohne Fehlermeldung. Arrow Function verwenden. - Vergessenes
"type": "module":importschlägt in Node mitCannot use import statement outside a modulefehl.
Lernergebnisse: Was Sie nach diesem Kapitel können sollten
Abschnitt betitelt „Lernergebnisse: Was Sie nach diesem Kapitel können sollten“Nach Abschluss dieses Kapitels sollten Schülerinnen und Schüler in der Lage sein:
- Anwenden: Node.js einrichten und JavaScript-Dateien ausführen.
- Anwenden: die Grundkonstrukte (Variablen, Kontrollstrukturen, Funktionen, Arrays, Objekte) sowie
map/filter/reducesicher einsetzen. - Anwenden: moderne Sprachmittel (Destructuring, Spread,
?.,??, Module) lesen und schreiben. - Erklären: den Nutzen statischer Typen erklären und die Fehlerklasse benennen, die TypeScript vor dem Programmlauf abfängt.
- Anwenden: Datenverträge als
interfacedefinieren, Funktionen annotieren und Union Types für Zustandsfelder einsetzen. - Nennen: die Rolle von Generics beim Lesen von Typen wie
Array<T>undPromise<T>nennen. - Beurteilen:
anyundunknownunterscheiden, den Umgang mit Daten unbekannter Form begründen und den Einsatz vonanyundasim eigenen wie im fremden Code kritisch bewerten.
Passende Übungen
Abschnitt betitelt „Passende Übungen“- Aufgabe 01 - TypeScript-Spickzettel
- Aufgabe 02 - Typen retten den Tag
- Aufgabe 03 - TypeScript-Katas