From 43e271f006cc32cda6d09929016bd3946c95875b Mon Sep 17 00:00:00 2001 From: Johannes Hoppe Date: Tue, 21 Apr 2026 22:53:48 +0200 Subject: [PATCH 01/12] material typescript: neue Intro, alle Abschnitte aktiviert - Hinweis-Box durch sanfteren Einstieg ersetzt, Zielgruppe (Crashkurs) klar benannt - sechs von Danny auskommentierte Abschnitte reaktiviert: Variablen, Template-Strings, Arrow Functions, Immutability, Spread/Rest, Promises/async-await --- material/typescript/README.md | 40 ++++++----------------------------- 1 file changed, 6 insertions(+), 34 deletions(-) diff --git a/material/typescript/README.md b/material/typescript/README.md index f9f6cea6..77f0a252 100644 --- a/material/typescript/README.md +++ b/material/typescript/README.md @@ -5,18 +5,14 @@ lastModified: 2026-02-05 hidden: true --- -> **Hinweis:** Dieser Artikel ist ein Zusatzmaterial zum [Angular-Buch](https://angular-buch.com). -> Im Buch setzen wir durchgehend auf **TypeScript** für die Entwicklung mit Angular. -> Dieses Kapitel bietet eine Einführung in die wichtigsten Sprachfeatures. -> -> Wenn du bereits Erfahrung mit TypeScript hast, kannst du dieses Kapitel überspringen. -> Viele Konzepte werden wir auch im Verlauf des Buchs praktisch kennenlernen. +Für die Entwicklung mit Angular verwenden wir die Programmiersprache **TypeScript**. +Dieser Artikel richtet sich an alle, die mit modernem JavaScript und TypeScript noch nicht vertraut sind. +Wir gehen die wichtigsten Sprachfeatures Schritt für Schritt durch und legen damit das Fundament für die Arbeit mit Angular. ---- +Keine Angst, du musst keine vollständig neue Sprache erlernen. +TypeScript baut auf JavaScript auf und ergänzt es um ein statisches Typsystem. -Für die Entwicklung mit Angular verwenden wir die Programmiersprache *TypeScript*. -Für Entwickler:innen, die bisher noch nicht mit TypeScript entwickelt haben, wollen wir hier einen kurzen Einstieg geben. -Keine Angst – du musst keine vollständig neue Sprache erlernen, um mit Angular arbeiten zu können, denn TypeScript ist eine Obermenge von JavaScript. +Wenn du schon Erfahrung mit modernem JavaScript oder TypeScript hast, kannst du diesen Crashkurs überspringen. TypeScript greift die aktuellen ECMAScript-Standards auf und integriert zusätzliche Features, unter anderem ein statisches Typsystem. Das bedeutet praktisch, dass die Typen von Variablen, Funktionsparametern und Klassen-Propertys direkt im Code aufgeschrieben werden. @@ -45,9 +41,6 @@ Dies ermöglicht Komfortfunktionen wie automatische Vervollständigung, Navigati Die meisten modernen IDEs wie Visual Studio Code oder IntelliJ/WebStorm unterstützen TypeScript nativ und ohne zusätzliche Plug-ins. In einem Angular-Projekt ist der TypeScript-Compiler außerdem schon vollständig konfiguriert, sodass wir sofort mit der Entwicklung beginnen können. - - - ## Die wichtigsten Basistypen @@ -339,9 +331,6 @@ class Document implements Printable { } ``` - - - - - - - - - - - - ## Private Eigenschaften von Klassen @@ -584,9 +560,6 @@ const title = signal('Angular'); // Signal const book = signal({ title: 'Angular' }); // Signal ``` - - - ## Weitere Features From 4d6c2dd32a30fd37d3ddbd7f7a9677aaad359fd4 Mon Sep 17 00:00:00 2001 From: Johannes Hoppe Date: Tue, 21 Apr 2026 23:14:35 +0200 Subject: [PATCH 02/12] =?UTF-8?q?material=20typescript:=20Redundanz=20bere?= =?UTF-8?q?inigt,=20Superset-Aussage=20pr=C3=A4zisiert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Doppelten Absatz nach Intro entfernt (wiederholte bereits die Intro) - "TypeScript einsetzen" gestrafft, Komfortfunktionen zusammengezogen - "Konfiguration" gestrafft (Transpilation steht schon oben) - Superset-Aussage präzisiert: TypeScript ist strenger als JavaScript (nur mit gelockerten Einstellungen nähert es sich reinem JS an) --- material/typescript/README.md | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/material/typescript/README.md b/material/typescript/README.md index 77f0a252..caff4d72 100644 --- a/material/typescript/README.md +++ b/material/typescript/README.md @@ -14,31 +14,26 @@ TypeScript baut auf JavaScript auf und ergänzt es um ein statisches Typsystem. Wenn du schon Erfahrung mit modernem JavaScript oder TypeScript hast, kannst du diesen Crashkurs überspringen. -TypeScript greift die aktuellen ECMAScript-Standards auf und integriert zusätzliche Features, unter anderem ein statisches Typsystem. -Das bedeutet praktisch, dass die Typen von Variablen, Funktionsparametern und Klassen-Propertys direkt im Code aufgeschrieben werden. -So erhalten wir schon während der Entwicklung eine gute Unterstützung im Editor und können unsere Software typsicher entwickeln. -Autovervollständigung, Typprüfungen und Rename Refactoring sind nur einige der Vorteile, die sich aus einem statischen Typsystem ergeben. - -Jedes Programm, das in JavaScript geschrieben wurde, funktioniert auch in TypeScript. -Dein bestehendes Wissen zu JavaScript bleibt also weiterhin anwendbar. - ## TypeScript einsetzen +TypeScript ist ein Open-Source-Projekt von Microsoft und basiert auf den aktuellen ECMAScript-Standards. +Die Sprache ist ein *Superset* von JavaScript: Jede JavaScript-Syntax ist auch in TypeScript gültig. +Allerdings ist TypeScript deutlich strenger als reines JavaScript. +Der Compiler prüft zusätzlich die Typen und lehnt Code ab, der in JavaScript noch durchgelaufen wäre. +In Angular-Projekten sind diese Prüfungen standardmäßig aktiviert (*strict mode*). +Nur wenn wir die Einstellungen gezielt lockern, nähert sich TypeScript dem Verhalten von reinem JavaScript an. + TypeScript ist nicht direkt im Browser lauffähig. -Deshalb wird der TypeScript-Code vor der Auslieferung wieder in JavaScript umgewandelt. +Deshalb wird der Code vor der Auslieferung wieder in JavaScript umgewandelt. Für diesen Prozess ist der TypeScript-Compiler verantwortlich. Man spricht dabei auch von *Transpilierung*, weil der Code lediglich in eine andere Sprache übertragen wird. Die statische Typisierung geht bei diesem Schritt verloren. -Das bedeutet, dass das Programm zur Laufzeit keine Typen mehr besitzt, denn es ist ein reines JavaScript-Programm. -Durch die Typunterstützung bei der Entwicklung und beim Build können allerdings schon die meisten Fehler erkannt und vermieden werden. - -TypeScript ist als Open-Source-Projekt bei der Firma Microsoft entstanden. -Durch die Typisierung können Fehler bereits zur Entwicklungszeit erkannt werden. -Außerdem können Tools den Code genauer analysieren. -Dies ermöglicht Komfortfunktionen wie automatische Vervollständigung, Navigation zwischen Methoden und Klassen, eine solide Refactoring-Unterstützung und automatische Dokumentation in der Entwicklungsumgebung. +Zur Laufzeit ist das Programm ein reines JavaScript-Programm ohne Typinformationen. +Durch die Typprüfungen bei der Entwicklung und beim Build können viele Fehler aber schon frühzeitig erkannt werden. Die meisten modernen IDEs wie Visual Studio Code oder IntelliJ/WebStorm unterstützen TypeScript nativ und ohne zusätzliche Plug-ins. +Neben der Fehlerprüfung profitieren wir dabei auch von Komfortfunktionen wie Autovervollständigung, Navigation zwischen Methoden und Klassen sowie einer soliden Refactoring-Unterstützung. In einem Angular-Projekt ist der TypeScript-Compiler außerdem schon vollständig konfiguriert, sodass wir sofort mit der Entwicklung beginnen können. ## Variablen: `const`, `let` und `var` @@ -622,10 +617,7 @@ console.log(zero ?? 'fallback'); // 0 ## Konfiguration -Um TypeScript-Code in Node.js oder im Browser ausführen zu können, muss dieser zunächst in JavaScript umgewandelt werden. -Diese Aufgabe übernimmt der *Transpiler*. - -Die Konfiguration wird in der Datei `tsconfig.json` hinterlegt. +Die Konfiguration des TypeScript-Compilers wird in der Datei `tsconfig.json` hinterlegt. Die wohl wichtigste Einstellung ist das `target`: Diese Option gibt an, in welche Version von JavaScript das Programm transpiliert werden soll. In einem Angular-Projekt müssen wir uns über die Konfiguration von TypeScript nur wenige Gedanken machen, denn die Einstellungen sind bereits mit sinnvollen Werten vordefiniert. From a1d096cae15ddbb7b43b0c03c0e077baf9911aa4 Mon Sep 17 00:00:00 2001 From: Johannes Hoppe Date: Wed, 22 Apr 2026 06:35:15 +0200 Subject: [PATCH 03/12] =?UTF-8?q?material=20typescript:=20Abschnitte=20in?= =?UTF-8?q?=20f=C3=BCnf=20Teile=20sortiert,=20Zusammenfassung=20aktualisie?= =?UTF-8?q?rt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Neue Reihenfolge folgt der Progression eines Crashkurses: - JavaScript-Basics (Variablen, Template-Strings, Arrow Functions, Spread/Rest, Immutability, Optional Chaining, Nullish Coalescing, Promises/async-await) - TypeScript-Typsystem (Basistypen, Union Types, Interfaces, Generics) - Objektorientierung (Klassen, Private, Property Modifiers) - Angular-spezifisch (Decorators) - Meta (Konfiguration, Zusammenfassung) "Weitere Features" wurde aufgelöst, die drei Unterabschnitte (Union Types, Optional Chaining, Nullish Coalescing) stehen jetzt als eigenständige h2-Abschnitte an den passenden Stellen. Die Zusammenfassung wurde an die neue Crashkurs-Zielgruppe angepasst und spricht nicht mehr Umsteiger von C#/Java an. Alle 32 Code-Beispiele und alle Textinhalte sind erhalten, nur die Position und drei Überschriftenebenen wurden verändert. --- material/typescript/README.md | 531 +++++++++++++++++----------------- 1 file changed, 264 insertions(+), 267 deletions(-) diff --git a/material/typescript/README.md b/material/typescript/README.md index caff4d72..7c53cb3b 100644 --- a/material/typescript/README.md +++ b/material/typescript/README.md @@ -101,6 +101,196 @@ Als Faustregel kannst du dir Folgendes merken: - Willst du den Wert später im Programm verändern, wähle `let`. - Nutze nicht `var`, denn du wirst es nicht benötigen. +## Template-Strings + +Mit einem normalen String in einfachen Anführungszeichen ist es nicht möglich, einen Text über mehrere Zeilen anzugeben. +Ein *Template-String* wird mit schrägen `` ` ``Hochkommata`` ` `` (*Backtick*) eingeleitet und beendet, nicht mit Anführungszeichen. +Der String kann sich über mehrere Zeilen erstrecken. + +Mit Template-Strings können wir außerdem Ausdrücke direkt in einen String einbetten: + +```typescript +const name = 'Angular'; +const version = 21; + +const message = `Willkommen bei ${name}! +Die aktuelle Version ist ${version}.`; +``` + +Wir werden Template-Strings vor allem nutzen, um URLs mit Parametern zusammenzubauen. + +## Arrow Functions + +Eine *Arrow-Funktion* ist eine Kurzschreibweise für eine normale `function()` in JavaScript. +Auch die Bezeichnung *Lambda-Ausdruck* ist verbreitet. + +Die Definition einer anonymen Funktion verkürzt sich damit elegant zu einem Pfeil `=>`. +Besitzt die Funktion genau einen Parameter ohne Typ, können die runden Klammern auf der linken Seite weggelassen werden. +Auch die geschweiften Klammern auf der rechten Seite können eingespart werden: +Lässt man die Klammern weg, ist das Ergebnis des rechtsseitigen Ausdrucks der Rückgabewert für die Funktion. + +```typescript +// Diese vier Definitionen sind gleichwertig: +const fn1 = function(x: number) { return x * 2; }; +const fn2 = (x: number) => { return x * 2; }; +const fn3 = (x: number) => x * 2; +const fn4 = x => x * 2; // Nur ohne Typangabe +``` + +Das folgende Beispiel zeigt, wie wir alle geraden Zahlen aus einer Liste ermitteln können: + +```typescript +const numbers = [1, 2, 3, 4, 5, 6]; + +// Herkömmliche Funktion +const even1 = numbers.filter(function(n) { + return n % 2 === 0; +}); + +// Arrow-Funktion – wesentlich kompakter +const even2 = numbers.filter(n => n % 2 === 0); +``` + +Ein weiterer Vorteil der Arrow-Funktion ist, dass sie keinen eigenen `this`-Kontext besitzt. +Das ist besonders dann interessant, wenn wir die Funktion innerhalb einer Klasse verwenden und mit `this` auf die Instanz der Klasse zugreifen möchten. +Mit Arrow-Funktionen wird die Variable `this` aus dem übergeordneten Kontext verwendet. + +```typescript +class Counter { + count = 0; + + increment() { + // Arrow-Funktion: this zeigt auf die Klasseninstanz + setTimeout(() => { + this.count++; + console.log(this.count); + }, 1000); + } +} +``` + +## Spread-Syntax und Rest-Parameter + +In JavaScript können wir eine Syntax mit drei Punkten verwenden (`...`). +Diese Schreibweise hat zwei Bedeutungen, je nachdem, wo sie eingesetzt wird: +Die *Spread-Syntax* breitet Elemente aus, während *Rest-Parameter* übrige Argumente einsammeln. + +### Objekteigenschaften kopieren + +Mit der Spread-Syntax können wir Objekte klonen und dabei Eigenschaften überschreiben: + +```typescript +const book = { title: 'Angular', year: 2023 }; +const copy = { ...book, year: 2026 }; + +console.log(book.year); // 2023 – Original unverändert +console.log(copy.year); // 2026 – Kopie mit neuem Wert +``` + +Bitte beachte, dass diese Idee nur für *Plain Objects* funktioniert und nur eine flache Kopie (*Shallow Copy*) erzeugt. +Tiefere Zweige eines Objekts müssen einzeln geklont werden. +Wird diese Aufgabe zu kompliziert, können wir die native Funktion `structuredClone()` verwenden, die eine *Deep Copy* erzeugt. + +### Array-Elemente kopieren + +Die Spread-Syntax funktioniert ähnlich auch für Arrays: + +```typescript +const arr1 = [1, 2, 3]; +const arr2 = [4, 5, 6]; + +const copy = [...arr1]; +const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6] +``` + +### Funktionsargumente übergeben + +Wollen wir die Elemente eines Arrays einzeln als Argumente an eine Funktion übergeben, können wir die Spread-Syntax nutzen: + +```typescript +const numbers = [1, 2, 3]; +console.log(Math.max(...numbers)); // 3 +``` + +### Funktionsargumente einsammeln + +In einem anderen Kontext haben die drei Punkte eine andere Bedeutung: +Erhält eine Funktion mehrere Argumente, so können wir diese elegant in einem Array erfassen. +Ein solcher Parameter heißt *Rest-Parameter*. + +```typescript +function sum(...numbers: number[]): number { + return numbers.reduce((a, b) => a + b, 0); +} + +console.log(sum(1, 2, 3, 4)); // 10 +``` + +## Immutability + +In JavaScript werden Objekte und Arrays stets nur als Referenzen auf eine zugehörige Speicherstelle gespeichert. +Ändern wir also die Inhalte direkt im Objekt, so ändert sich die Referenz nicht! +Das bedeutet auch, dass bei Zuweisung eines Objekts zu einer Variable lediglich ein Verweis auf das ursprüngliche Objekt erzeugt wird. + +```typescript +const book = { title: 'Angular', year: 2023 }; +const copy = book; +copy.year = 2024; + +console.log(book.year); // 2024 – auch das Original wurde geändert! +``` + +Um gut wartbaren Code zu erhalten, dürfen wir niemals die Werte eines Objekts oder Arrays direkt verändern. +Wir behandeln ein Objekt oder Array als *unveränderlich* (engl. *immutable*) und erzeugen bei einer Änderung immer eine Kopie. +Hierfür nutzen wir in der Regel die Spread-Syntax. + +> **Merke:** Objekte und Arrays sollten nie direkt verändert werden. Stattdessen sollte immer eine Kopie mit neuer Referenz erzeugt werden, die die gewünschten Änderungen enthält. + +## Optional Chaining + +Optional Chaining ermöglicht einen sicheren Zugriff auf verschachtelte Objekte: + +```typescript +const user = { address: { city: 'Berlin' } }; +const city = user?.address?.city; // 'Berlin' +const zip = user?.address?.zip; // undefined (kein Fehler) +``` + +## Nullish Coalescing + +Nullish Coalescing erlaubt die einfache Zuweisung von Rückfallwerten: + +```typescript +const value = null; +const result = value ?? 'default'; // 'default' + +// Unterschied zu || +const zero = 0; +console.log(zero || 'fallback'); // 'fallback' +console.log(zero ?? 'fallback'); // 0 +``` + +## Promises und `async`/`await` + +Eine *Promise* ist ein natives Objekt in JavaScript, das einen asynchronen Vorgang repräsentiert. +Sie liefert entweder einen Wert zurück, wenn die Operation erfolgreich war, oder einen Fehler, wenn die Ausführung fehlgeschlagen ist. + +Mit den Schlüsselwörtern `async` und `await` können wir asynchronen Code schreiben, der wie synchroner Code aussieht. + +```typescript +// Mit then() +fetch('/api/data') + .then(response => response.json()) + .then(data => console.log(data)); + +// Mit async/await +async function loadData() { + const response = await fetch('/api/data'); + const data = await response.json(); + console.log(data); +} +``` + ## Die wichtigsten Basistypen Die starke Typisierung ermöglicht es, die Schnittstellen der Software genau zu beschreiben. @@ -171,6 +361,75 @@ if (typeof value === 'string') { Praktisch solltest du es vermeiden, `any` zu verwenden, denn dieser Typ ist fast immer ein Indiz dafür, dass Unklarheit über die Typisierung herrscht. Willst du die konkrete Belegung einer Variable absichtlich im Unklaren lassen, ist `unknown` die bessere Wahl. +## Union Types + +Mit Union Types können wir zusammengesetzte Typen beschreiben: + +```typescript +function format(value: string | number): string { + if (typeof value === 'string') { + return value.toUpperCase(); + } + return value.toFixed(2); +} +``` + +## Interfaces + +Um die Typisierung in unserem Programmcode konsequent umzusetzen, stellt TypeScript sogenannte *Interfaces* bereit. +Interfaces dienen dazu, die typisierte Struktur eines Objekts zu definieren, nicht jedoch die Werte. +Optionale Eigenschaften werden durch ein Fragezeichen-Symbol gekennzeichnet. + +```typescript +interface User { + firstname: string; + lastname: string; + age?: number; +} + +const user: User = { + firstname: 'Max', + lastname: 'Mustermann' +}; +``` + +Fügen wir dem Objekt eine zusätzliche Eigenschaft hinzu oder hat eine der Eigenschaften nicht den Typ, der im Interface definiert wurde, so erhalten wir einen Fehler. + +### Interface für Klassen + +Interfaces können auch dafür verwendet werden, die Struktur einer Klasse vorzugeben. +Dafür wird nach dem Klassennamen das Schlüsselwort `implements` angefügt. + +```typescript +interface Printable { + print(): void; +} + +class Document implements Printable { + print(): void { + console.log('Printing...'); + } +} +``` + +## Generic Types + +Mit *Generics* können wir Typparameter für Klassen und Funktionen definieren. +Sie sind ein wichtiges Konzept in TypeScript, um wiederverwendbare und flexible Funktionen zu erstellen. + +```typescript +interface Book { + title: string; +} + +// Generischer Typ wird automatisch erkannt +const count = signal(0); // Signal +const title = signal('Angular'); // Signal + +// Bei Objekten muss der Typ explizit angegeben werden +const book = signal({ title: 'Angular' }); // Signal +``` + ## Klassen Um eine Klasse zu beschreiben, verwenden wir in JavaScript und TypeScript das Schlüsselwort `class`. @@ -288,189 +547,6 @@ class PowerUser extends User { Mit `super()` kann der Konstruktor der Basisklasse ausgeführt werden. -## Interfaces - -Um die Typisierung in unserem Programmcode konsequent umzusetzen, stellt TypeScript sogenannte *Interfaces* bereit. -Interfaces dienen dazu, die typisierte Struktur eines Objekts zu definieren, nicht jedoch die Werte. -Optionale Eigenschaften werden durch ein Fragezeichen-Symbol gekennzeichnet. - -```typescript -interface User { - firstname: string; - lastname: string; - age?: number; -} - -const user: User = { - firstname: 'Max', - lastname: 'Mustermann' -}; -``` - -Fügen wir dem Objekt eine zusätzliche Eigenschaft hinzu oder hat eine der Eigenschaften nicht den Typ, der im Interface definiert wurde, so erhalten wir einen Fehler. - -### Interface für Klassen - -Interfaces können auch dafür verwendet werden, die Struktur einer Klasse vorzugeben. -Dafür wird nach dem Klassennamen das Schlüsselwort `implements` angefügt. - -```typescript -interface Printable { - print(): void; -} - -class Document implements Printable { - print(): void { - console.log('Printing...'); - } -} -``` - -## Template-Strings - -Mit einem normalen String in einfachen Anführungszeichen ist es nicht möglich, einen Text über mehrere Zeilen anzugeben. -Ein *Template-String* wird mit schrägen `` ` ``Hochkommata`` ` `` (*Backtick*) eingeleitet und beendet, nicht mit Anführungszeichen. -Der String kann sich über mehrere Zeilen erstrecken. - -Mit Template-Strings können wir außerdem Ausdrücke direkt in einen String einbetten: - -```typescript -const name = 'Angular'; -const version = 21; - -const message = `Willkommen bei ${name}! -Die aktuelle Version ist ${version}.`; -``` - -Wir werden Template-Strings vor allem nutzen, um URLs mit Parametern zusammenzubauen. - -## Arrow Functions - -Eine *Arrow-Funktion* ist eine Kurzschreibweise für eine normale `function()` in JavaScript. -Auch die Bezeichnung *Lambda-Ausdruck* ist verbreitet. - -Die Definition einer anonymen Funktion verkürzt sich damit elegant zu einem Pfeil `=>`. -Besitzt die Funktion genau einen Parameter ohne Typ, können die runden Klammern auf der linken Seite weggelassen werden. -Auch die geschweiften Klammern auf der rechten Seite können eingespart werden: -Lässt man die Klammern weg, ist das Ergebnis des rechtsseitigen Ausdrucks der Rückgabewert für die Funktion. - -```typescript -// Diese vier Definitionen sind gleichwertig: -const fn1 = function(x: number) { return x * 2; }; -const fn2 = (x: number) => { return x * 2; }; -const fn3 = (x: number) => x * 2; -const fn4 = x => x * 2; // Nur ohne Typangabe -``` - -Das folgende Beispiel zeigt, wie wir alle geraden Zahlen aus einer Liste ermitteln können: - -```typescript -const numbers = [1, 2, 3, 4, 5, 6]; - -// Herkömmliche Funktion -const even1 = numbers.filter(function(n) { - return n % 2 === 0; -}); - -// Arrow-Funktion – wesentlich kompakter -const even2 = numbers.filter(n => n % 2 === 0); -``` - -Ein weiterer Vorteil der Arrow-Funktion ist, dass sie keinen eigenen `this`-Kontext besitzt. -Das ist besonders dann interessant, wenn wir die Funktion innerhalb einer Klasse verwenden und mit `this` auf die Instanz der Klasse zugreifen möchten. -Mit Arrow-Funktionen wird die Variable `this` aus dem übergeordneten Kontext verwendet. - -```typescript -class Counter { - count = 0; - - increment() { - // Arrow-Funktion: this zeigt auf die Klasseninstanz - setTimeout(() => { - this.count++; - console.log(this.count); - }, 1000); - } -} -``` - -## Immutability - -In JavaScript werden Objekte und Arrays stets nur als Referenzen auf eine zugehörige Speicherstelle gespeichert. -Ändern wir also die Inhalte direkt im Objekt, so ändert sich die Referenz nicht! -Das bedeutet auch, dass bei Zuweisung eines Objekts zu einer Variable lediglich ein Verweis auf das ursprüngliche Objekt erzeugt wird. - -```typescript -const book = { title: 'Angular', year: 2023 }; -const copy = book; -copy.year = 2024; - -console.log(book.year); // 2024 – auch das Original wurde geändert! -``` - -Um gut wartbaren Code zu erhalten, dürfen wir niemals die Werte eines Objekts oder Arrays direkt verändern. -Wir behandeln ein Objekt oder Array als *unveränderlich* (engl. *immutable*) und erzeugen bei einer Änderung immer eine Kopie. -Hierfür nutzen wir in der Regel die Spread-Syntax. - -> **Merke:** Objekte und Arrays sollten nie direkt verändert werden. Stattdessen sollte immer eine Kopie mit neuer Referenz erzeugt werden, die die gewünschten Änderungen enthält. - -## Spread-Syntax und Rest-Parameter - -In JavaScript können wir eine Syntax mit drei Punkten verwenden (`...`). -Diese Schreibweise hat zwei Bedeutungen, je nachdem, wo sie eingesetzt wird: -Die *Spread-Syntax* breitet Elemente aus, während *Rest-Parameter* übrige Argumente einsammeln. - -### Objekteigenschaften kopieren - -Mit der Spread-Syntax können wir Objekte klonen und dabei Eigenschaften überschreiben: - -```typescript -const book = { title: 'Angular', year: 2023 }; -const copy = { ...book, year: 2026 }; - -console.log(book.year); // 2023 – Original unverändert -console.log(copy.year); // 2026 – Kopie mit neuem Wert -``` - -Bitte beachte, dass diese Idee nur für *Plain Objects* funktioniert und nur eine flache Kopie (*Shallow Copy*) erzeugt. -Tiefere Zweige eines Objekts müssen einzeln geklont werden. -Wird diese Aufgabe zu kompliziert, können wir die native Funktion `structuredClone()` verwenden, die eine *Deep Copy* erzeugt. - -### Array-Elemente kopieren - -Die Spread-Syntax funktioniert ähnlich auch für Arrays: - -```typescript -const arr1 = [1, 2, 3]; -const arr2 = [4, 5, 6]; - -const copy = [...arr1]; -const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6] -``` - -### Funktionsargumente übergeben - -Wollen wir die Elemente eines Arrays einzeln als Argumente an eine Funktion übergeben, können wir die Spread-Syntax nutzen: - -```typescript -const numbers = [1, 2, 3]; -console.log(Math.max(...numbers)); // 3 -``` - -### Funktionsargumente einsammeln - -In einem anderen Kontext haben die drei Punkte eine andere Bedeutung: -Erhält eine Funktion mehrere Argumente, so können wir diese elegant in einem Array erfassen. -Ein solcher Parameter heißt *Rest-Parameter*. - -```typescript -function sum(...numbers: number[]): number { - return numbers.reduce((a, b) => a + b, 0); -} - -console.log(sum(1, 2, 3, 4)); // 10 -``` - ## Private Eigenschaften von Klassen Mit *Private Class Fields* in JavaScript können wir Datenkapselung in Klassen realisieren. @@ -537,84 +613,6 @@ Angular nutzt dieses Sprachkonzept, um Klassen eine Semantik zu geben: Durch den Decorator `@Component()` wird diese Klasse als Komponente behandelt. Alle Decorators von Angular sind Funktionen, daher darf man die Funktionsklammern bei der Verwendung nicht vergessen. -## Generic Types - -Mit *Generics* können wir Typparameter für Klassen und Funktionen definieren. -Sie sind ein wichtiges Konzept in TypeScript, um wiederverwendbare und flexible Funktionen zu erstellen. - -```typescript -interface Book { - title: string; -} - -// Generischer Typ wird automatisch erkannt -const count = signal(0); // Signal -const title = signal('Angular'); // Signal - -// Bei Objekten muss der Typ explizit angegeben werden -const book = signal({ title: 'Angular' }); // Signal -``` - -## Promises und `async`/`await` - -Eine *Promise* ist ein natives Objekt in JavaScript, das einen asynchronen Vorgang repräsentiert. -Sie liefert entweder einen Wert zurück, wenn die Operation erfolgreich war, oder einen Fehler, wenn die Ausführung fehlgeschlagen ist. - -Mit den Schlüsselwörtern `async` und `await` können wir asynchronen Code schreiben, der wie synchroner Code aussieht. - -```typescript -// Mit then() -fetch('/api/data') - .then(response => response.json()) - .then(data => console.log(data)); - -// Mit async/await -async function loadData() { - const response = await fetch('/api/data'); - const data = await response.json(); - console.log(data); -} -``` - -## Weitere Features - -### Union Types - -Mit Union Types können wir zusammengesetzte Typen beschreiben: - -```typescript -function format(value: string | number): string { - if (typeof value === 'string') { - return value.toUpperCase(); - } - return value.toFixed(2); -} -``` - -### Optional Chaining - -Optional Chaining ermöglicht einen sicheren Zugriff auf verschachtelte Objekte: - -```typescript -const user = { address: { city: 'Berlin' } }; -const city = user?.address?.city; // 'Berlin' -const zip = user?.address?.zip; // undefined (kein Fehler) -``` - -### Nullish Coalescing - -Nullish Coalescing erlaubt die einfache Zuweisung von Rückfallwerten: - -```typescript -const value = null; -const result = value ?? 'default'; // 'default' - -// Unterschied zu || -const zero = 0; -console.log(zero || 'fallback'); // 'fallback' -console.log(zero ?? 'fallback'); // 0 -``` - ## Konfiguration Die Konfiguration des TypeScript-Compilers wird in der Datei `tsconfig.json` hinterlegt. @@ -624,11 +622,10 @@ In einem Angular-Projekt müssen wir uns über die Konfiguration von TypeScript ## Zusammenfassung -TypeScript erweitert den JavaScript-Sprachstandard um viele Features, die wir bereits aus etablierten Sprachen wie C# oder Java kennen. -Dadurch fällt auch der Umstieg von einer anderen objektorientierten Sprache nicht schwer. -Auch wenn du bisher mit reinem JavaScript entwickelt hast, ist der Umstieg auf TypeScript keine große Hürde, weil alle bekannten Features aus JavaScript weiterhin verwendet werden können. +Mit diesem Crashkurs haben wir die wichtigsten Bausteine von TypeScript kennengelernt: moderne Sprachfeatures aus JavaScript wie `const`/`let`, Arrow Functions und die Spread-Syntax, dazu das Typsystem von TypeScript mit Interfaces, Union Types und Generics, und schließlich die Objektorientierung mit Klassen und Property Modifiers. -Mit der Typisierung und Objektorientierung können wir die Schnittstellen unserer Software klar definieren. -Der Editor kann uns bei der Arbeit mit TypeScript effizient unterstützen und schon zur Entwicklungszeit auf Fehler hinweisen. +TypeScript ist strenger als JavaScript – und genau das macht die Sprache so wertvoll. +Die Typprüfung im Compiler und die Unterstützung durch die IDE helfen uns, Fehler früh zu erkennen und Software wartbar zu entwickeln. -Damit unsere Anwendung später auch in jedem Browser lauffähig ist, wird TypeScript vor der Auslieferung immer in reines JavaScript umgewandelt. +In einem Angular-Projekt ist TypeScript bereits vollständig konfiguriert, sodass wir sofort loslegen können. +Dieser Artikel hat das Fundament gelegt – im Angular-Buch bauen wir darauf auf und wenden die Features praktisch an. From a6cb6538683c260096c5778cbad2ef3129e85b10 Mon Sep 17 00:00:00 2001 From: Johannes Hoppe Date: Tue, 12 May 2026 13:46:15 +0200 Subject: [PATCH 04/12] =?UTF-8?q?material=20typescript:=20Crashkurs=20?= =?UTF-8?q?=C3=BCberarbeitet,=20Beispiele=20erweitert,=20Reihenfolge=20fix?= =?UTF-8?q?iert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 19 inhaltliche Verbesserungen aus einer kompletten Lesung mit Einsteiger-Brille: Erklärungen und Begriffe: - Mini-Transpilierungs-Beispiel zeigt vorher/nachher - Zweites Transpilier-Beispiel mit Konstruktor-Kurzschreibweise - "nullish" kurz erklärt vor Nullish Coalescing - Synchron vs. asynchron erklärt vor Promises - "Metadaten" bei Decorators erklärt Reihenfolge und Beispiele: - Variablen-Abschnitt komplett neu: erst const, dann let, dann kurzer var-Hinweis (Wortspiel "var-heit" entfernt) - Immutability vor Spread (Problem vor Lösung) - Schmerz-Beispiel bei Optional Chaining mit explizitem | undefined-Typ - URL-Beispiel aus Listing 20.1 bei Template-Strings (vereinfacht) - "drei Bestandteile" bei Klassen entschärft - Getter/Setter ohne #, mit echtem Setter-Beispiel, "Methoden verstecken" umformuliert Inhaltliche Erweiterungen: - this-Kontext bei Arrow Functions mit Problem-Code und Lösungs-Code - unknown-Use-Case mit catch (error: unknown) - Union Types: String-Literal-Union als zweites Beispiel - Generics: eigene generische Funktion first vor signal() - Decorators: @Directive, @Pipe und @Service ergänzt - private vs # entschärft, beide als gültig markiert Konfiguration und Fakten: - "Schnittstellen" -> "Datenstrukturen" (vermeidet Konfusion mit Interfaces) - "wichtigste Einstellung ist target" korrigiert: strict ist heute zentraler, target nachgelagert - fn4-Kommentar bei Arrow Functions präzisiert --- material/typescript/README.md | 259 ++++++++++++++++++++++++---------- 1 file changed, 183 insertions(+), 76 deletions(-) diff --git a/material/typescript/README.md b/material/typescript/README.md index 7c53cb3b..3359ba05 100644 --- a/material/typescript/README.md +++ b/material/typescript/README.md @@ -27,56 +27,52 @@ TypeScript ist nicht direkt im Browser lauffähig. Deshalb wird der Code vor der Auslieferung wieder in JavaScript umgewandelt. Für diesen Prozess ist der TypeScript-Compiler verantwortlich. Man spricht dabei auch von *Transpilierung*, weil der Code lediglich in eine andere Sprache übertragen wird. +Konkret bedeutet das: Die Typangaben werden beim Transpilieren entfernt. -Die statische Typisierung geht bei diesem Schritt verloren. -Zur Laufzeit ist das Programm ein reines JavaScript-Programm ohne Typinformationen. -Durch die Typprüfungen bei der Entwicklung und beim Build können viele Fehler aber schon frühzeitig erkannt werden. - -Die meisten modernen IDEs wie Visual Studio Code oder IntelliJ/WebStorm unterstützen TypeScript nativ und ohne zusätzliche Plug-ins. -Neben der Fehlerprüfung profitieren wir dabei auch von Komfortfunktionen wie Autovervollständigung, Navigation zwischen Methoden und Klassen sowie einer soliden Refactoring-Unterstützung. -In einem Angular-Projekt ist der TypeScript-Compiler außerdem schon vollständig konfiguriert, sodass wir sofort mit der Entwicklung beginnen können. - -## Variablen: `const`, `let` und `var` - -Ursprünglich wurden Variablen in JavaScript mit dem Schlüsselwort `var` eingeleitet. -Das funktioniert noch immer, allerdings kamen mit ECMAScript 2015 die neuen Variablenarten `let` und `const` hinzu. +```typescript +// TypeScript-Quellcode: +let age: number = 30; -### Die schmerzhafte `var`-heit +// Nach der Transpilierung (JavaScript): +let age = 30; +``` -Mit dem Schlüsselwort `var` eingeleitete Variablen sind jeweils in der Funktion gültig, in der sie auch deklariert wurden – und zwar überall. -Variablen mit `var` „fressen" sich durch alle Blöcke hindurch und sind in der *gesamten* Funktion und in allen darin verschachtelten Blöcken und Funktionen verfügbar. +Darüber hinaus kennt TypeScript eine Reihe von eigenen Syntaxen, die beim Transpilieren in äquivalenten JavaScript-Code aufgelöst werden. +Ein Beispiel dafür ist die Konstruktor-Kurzschreibweise: ```typescript -function example() { - if (true) { - var x = 10; +// TypeScript-Kurzschreibweise im Konstruktor: +class User { + constructor(public name: string) {} +} + +// Nach der Transpilierung (JavaScript): +class User { + constructor(name) { + this.name = name; } - console.log(x); // 10 – x ist hier verfügbar! } ``` -Diese Eigenschaft führt in der Praxis schnell zu Kollisionen von Variablen aus verschiedenen Programmteilen. +Diese Schreibweise schauen wir uns später im Abschnitt zu Klassen genauer an. -### Blockgebundene Variablen mit `let` +Die statische Typisierung geht bei diesem Schritt verloren. +Zur Laufzeit ist das Programm ein reines JavaScript-Programm ohne Typinformationen. +Durch die Typprüfungen bei der Entwicklung und beim Build können viele Fehler aber schon frühzeitig erkannt werden. -Mit Einführung von ECMAScript 2015 hielt der Variablentyp `let` Einzug in die Webentwicklung. -Damit lassen sich blockgebundene Variablen definieren. -Sie sind nicht in der gesamten Funktion gültig, sondern lediglich innerhalb des Blocks, in dem sie definiert wurden. +Die meisten modernen IDEs wie Visual Studio Code oder IntelliJ/WebStorm unterstützen TypeScript nativ und ohne zusätzliche Plug-ins. +Neben der Fehlerprüfung profitieren wir dabei auch von Komfortfunktionen wie Autovervollständigung, Navigation zwischen Methoden und Klassen sowie einer soliden Refactoring-Unterstützung. +In einem Angular-Projekt ist der TypeScript-Compiler außerdem schon vollständig konfiguriert, sodass wir sofort mit der Entwicklung beginnen können. -```typescript -for (let i = 0; i < 10; i++) { - // i ist nur hier gültig -} -// console.log(i); // Fehler: i ist nicht definiert -``` +## Variablen: `const` und `let` + +In JavaScript und TypeScript deklarieren wir Variablen mit den Schlüsselwörtern `const` und `let`. +Beide wurden mit ECMAScript 2015 eingeführt und unterscheiden sich darin, ob sich der Wert nach der Initialisierung noch ändern lässt. ### Konstanten mit `const` -Variablen, die mit `var` oder `let` eingeleitet werden, lassen sich jederzeit überschreiben. -Häufig ändert sich der Wert einer Variable allerdings nach der Initialisierung nicht mehr. -Für solche Fälle gibt es Konstanten. -Sie werden mit dem Schlüsselwort `const` eingeleitet. -Wird eine Konstante einmal festgelegt, so lässt sich der Wert nicht mehr überschreiben. +Variablen, deren Wert sich nach der Initialisierung nicht mehr ändern soll, deklarieren wir mit `const`. +In der Praxis ist das der häufigste Fall — wir empfehlen, eine Variable zunächst immer mit `const` zu deklarieren. ```typescript const name = 'Angular'; @@ -93,13 +89,22 @@ book.title = 'Angular Buch'; // Das funktioniert! // book = { title: 'Neues Buch' }; // Fehler: Zuweisung nicht möglich ``` -### Wann nutze ich welche? +### Variablen mit `let` -Als Faustregel kannst du dir Folgendes merken: +Soll sich der Wert einer Variable während des Programmablaufs ändern, deklarieren wir sie mit `let`. +Mit `let` deklarierte Variablen sind blockgebunden: Sie gelten nur innerhalb des Blocks, in dem sie deklariert wurden — typischerweise zwischen geschweiften Klammern wie einer Schleife oder einem `if`-Block. -- Nutze zunächst immer `const`. -- Willst du den Wert später im Programm verändern, wähle `let`. -- Nutze nicht `var`, denn du wirst es nicht benötigen. +```typescript +for (let i = 0; i < 10; i++) { + // i ist nur hier gültig +} +// console.log(i); // Fehler: i ist nicht definiert +``` + +### Hinweis: das alte `var` + +In älterem JavaScript-Code begegnet uns noch das Schlüsselwort `var`, das vor ECMAScript 2015 die einzige Möglichkeit war, eine Variable zu deklarieren. +Im modernen Alltag benötigen wir es nicht mehr — wir verwenden ausschließlich `const` und `let`. ## Template-Strings @@ -118,6 +123,17 @@ Die aktuelle Version ist ${version}.`; ``` Wir werden Template-Strings vor allem nutzen, um URLs mit Parametern zusammenzubauen. +Genau für ein solches Szenario setzen wir Template-Strings auch im Angular-Buch ein, etwa um in einem Service die API-URL zusammenzubauen: + +```typescript +// Vereinfacht aus Listing 20.1: +const apiUrl = 'https://api1.angular-buch.com'; +const url = `${apiUrl}/books`; +// 'https://api1.angular-buch.com/books' +``` + +Im Buch arbeiten wir an dieser Stelle bereits mit dem Angular-`HttpClient` und Observables. +Für den Moment ist hier nur wichtig, dass der Template-String die Basis-URL mit dem Pfad `/books` zu einer vollständigen URL kombiniert. ## Arrow Functions @@ -134,7 +150,7 @@ Lässt man die Klammern weg, ist das Ergebnis des rechtsseitigen Ausdrucks der R const fn1 = function(x: number) { return x * 2; }; const fn2 = (x: number) => { return x * 2; }; const fn3 = (x: number) => x * 2; -const fn4 = x => x * 2; // Nur ohne Typangabe +const fn4 = x => x * 2; // Klammern weglassen geht nur, wenn kein Typ notiert wird ``` Das folgende Beispiel zeigt, wie wir alle geraden Zahlen aus einer Liste ermitteln können: @@ -151,24 +167,68 @@ const even1 = numbers.filter(function(n) { const even2 = numbers.filter(n => n % 2 === 0); ``` -Ein weiterer Vorteil der Arrow-Funktion ist, dass sie keinen eigenen `this`-Kontext besitzt. -Das ist besonders dann interessant, wenn wir die Funktion innerhalb einer Klasse verwenden und mit `this` auf die Instanz der Klasse zugreifen möchten. -Mit Arrow-Funktionen wird die Variable `this` aus dem übergeordneten Kontext verwendet. +### Der `this`-Kontext + +In JavaScript bezieht sich `this` innerhalb einer Methode normalerweise auf die Klasseninstanz, zu der die Methode gehört. +Bei einer klassischen Funktion mit dem Schlüsselwort `function` ändert sich dieser Bezug allerdings je nach Aufrufkontext — und das führt schnell zu Fehlern. + +Im folgenden Beispiel kennt die Funktion in `setTimeout` die Klasseninstanz nicht mehr: + +```typescript +class Counter { + count = 0; + + increment() { + setTimeout(function() { + this.count++; // Fehler: this ist hier nicht der Counter! + }, 1000); + } +} +``` + +Genau dieses Problem lösen Arrow-Funktionen: Sie besitzen keinen eigenen `this`-Kontext, sondern übernehmen `this` aus dem umgebenden Code. +Mit einer Arrow-Funktion in `setTimeout` zeigt `this` weiterhin auf die Klasseninstanz: ```typescript class Counter { count = 0; increment() { - // Arrow-Funktion: this zeigt auf die Klasseninstanz setTimeout(() => { this.count++; - console.log(this.count); + console.log(this.count); // OK: this zeigt auf den Counter }, 1000); } } ``` +## Immutability + +In JavaScript werden Objekte und Arrays stets nur als Referenzen auf eine zugehörige Speicherstelle gespeichert. +Ändern wir also die Inhalte direkt im Objekt, so ändert sich die Referenz nicht! +Das bedeutet auch, dass bei Zuweisung eines Objekts zu einer Variable lediglich ein Verweis auf das ursprüngliche Objekt erzeugt wird. + +```typescript +const book = { title: 'Angular', year: 2023 }; +const copy = book; +copy.year = 2024; + +console.log(book.year); // 2024 – auch das Original wurde geändert! +``` + +Um gut wartbaren Code zu erhalten, dürfen wir niemals die Werte eines Objekts oder Arrays direkt verändern. +Wir behandeln ein Objekt oder Array als *unveränderlich* (engl. *immutable*) und erzeugen bei einer Änderung immer eine Kopie. +Hierfür nutzen wir in der Regel die Spread-Syntax: + +```typescript +const book = { title: 'Angular', year: 2023 }; +const updated = { ...book, year: 2026 }; // Kopie mit neuem Wert +``` + +> **Merke:** Objekte und Arrays sollten nie direkt verändert werden. Stattdessen sollte immer eine Kopie mit neuer Referenz erzeugt werden, die die gewünschten Änderungen enthält. + +Wie die Spread-Syntax genau funktioniert, schauen wir uns jetzt an. + ## Spread-Syntax und Rest-Parameter In JavaScript können wir eine Syntax mit drei Punkten verwenden (`...`). @@ -226,39 +286,32 @@ function sum(...numbers: number[]): number { console.log(sum(1, 2, 3, 4)); // 10 ``` -## Immutability +## Optional Chaining -In JavaScript werden Objekte und Arrays stets nur als Referenzen auf eine zugehörige Speicherstelle gespeichert. -Ändern wir also die Inhalte direkt im Objekt, so ändert sich die Referenz nicht! -Das bedeutet auch, dass bei Zuweisung eines Objekts zu einer Variable lediglich ein Verweis auf das ursprüngliche Objekt erzeugt wird. +Wenn ein verschachteltes Objekt eine optionale Eigenschaft hat, könnte der Zugriff darauf fehlschlagen — TypeScript warnt uns davor: ```typescript -const book = { title: 'Angular', year: 2023 }; -const copy = book; -copy.year = 2024; +type User = { + address: { city: string } | undefined; +}; -console.log(book.year); // 2024 – auch das Original wurde geändert! +const user: User = { address: undefined }; +const city = user.address.city; +// Fehler: 'user.address' ist möglicherweise 'undefined' ``` -Um gut wartbaren Code zu erhalten, dürfen wir niemals die Werte eines Objekts oder Arrays direkt verändern. -Wir behandeln ein Objekt oder Array als *unveränderlich* (engl. *immutable*) und erzeugen bei einer Änderung immer eine Kopie. -Hierfür nutzen wir in der Regel die Spread-Syntax. - -> **Merke:** Objekte und Arrays sollten nie direkt verändert werden. Stattdessen sollte immer eine Kopie mit neuer Referenz erzeugt werden, die die gewünschten Änderungen enthält. - -## Optional Chaining - -Optional Chaining ermöglicht einen sicheren Zugriff auf verschachtelte Objekte: +Mit Optional Chaining können wir solche Zugriffe absichern. +Der `?.`-Operator liefert `undefined`, wenn die linke Seite nicht existiert, statt einen Fehler zu werfen: ```typescript -const user = { address: { city: 'Berlin' } }; -const city = user?.address?.city; // 'Berlin' -const zip = user?.address?.zip; // undefined (kein Fehler) +const user: User = { address: undefined }; +const city = user.address?.city; // string | undefined ``` ## Nullish Coalescing -Nullish Coalescing erlaubt die einfache Zuweisung von Rückfallwerten: +Als *nullish* gelten in JavaScript die Werte `null` und `undefined`. +Der `??`-Operator (Nullish Coalescing) liefert einen Rückfallwert, wenn der linke Wert nullish ist: ```typescript const value = null; @@ -272,6 +325,10 @@ console.log(zero ?? 'fallback'); // 0 ## Promises und `async`/`await` +Manche Vorgänge brauchen Zeit — zum Beispiel ein Netzwerk-Aufruf an einen Server. +JavaScript wartet darauf nicht, sondern führt den restlichen Code weiter aus und meldet sich später mit dem Ergebnis. +Solche Vorgänge nennt man *asynchron*. + Eine *Promise* ist ein natives Objekt in JavaScript, das einen asynchronen Vorgang repräsentiert. Sie liefert entweder einen Wert zurück, wenn die Operation erfolgreich war, oder einen Fehler, wenn die Ausführung fehlgeschlagen ist. @@ -293,7 +350,7 @@ async function loadData() { ## Die wichtigsten Basistypen -Die starke Typisierung ermöglicht es, die Schnittstellen der Software genau zu beschreiben. +Die starke Typisierung ermöglicht es, die Datenstrukturen unserer Anwendung präzise zu beschreiben. So können schon während der Entwicklung hilfreiche Informationen und Warnungen bereitgestellt werden, wenn die API nicht korrekt verwendet wird. ### Primitive Typen: Zahlen, Zeichenketten und boolesche Werte @@ -361,6 +418,19 @@ if (typeof value === 'string') { Praktisch solltest du es vermeiden, `any` zu verwenden, denn dieser Typ ist fast immer ein Indiz dafür, dass Unklarheit über die Typisierung herrscht. Willst du die konkrete Belegung einer Variable absichtlich im Unklaren lassen, ist `unknown` die bessere Wahl. +In der Praxis begegnet uns `unknown` vor allem in `catch`-Blöcken. +Da ein Fehler von beliebigem Typ sein kann, ist die `error`-Variable standardmäßig als `unknown` typisiert: + +```typescript +try { + // riskante Operation +} catch (error: unknown) { + if (error instanceof Error) { + console.log(error.message); + } +} +``` + ## Union Types Mit Union Types können wir zusammengesetzte Typen beschreiben: @@ -374,6 +444,16 @@ function format(value: string | number): string { } ``` +Häufig kombinieren wir auch mehrere String-Literale zu einem Union Type, um eine begrenzte Auswahl an Werten festzulegen: + +```typescript +type Status = 'loading' | 'success' | 'error'; + +let currentStatus: Status = 'loading'; +currentStatus = 'success'; // OK +// currentStatus = 'pending'; // Fehler: 'pending' ist nicht zulässig +``` + ## Interfaces Um die Typisierung in unserem Programmcode konsequent umzusetzen, stellt TypeScript sogenannte *Interfaces* bereit. @@ -417,6 +497,21 @@ class Document implements Printable { Mit *Generics* können wir Typparameter für Klassen und Funktionen definieren. Sie sind ein wichtiges Konzept in TypeScript, um wiederverwendbare und flexible Funktionen zu erstellen. +Im einfachsten Fall definieren wir eine Funktion mit einem Typparameter `T`, der beim Aufruf automatisch ermittelt wird: + +```typescript +// Eine generische Funktion mit Typparameter T +function first(arr: T[]): T { + return arr[0]; +} + +const firstBook = first(['Angular', 'React']); // string +const firstNumber = first([1, 2, 3]); // number +``` + +Auch im Angular-Ökosystem begegnen uns Generics häufig. +Die Funktion `signal()` (mehr dazu im Buch) erzeugt einen reaktiven Wert mit generischem Typ: + ```typescript interface Book { title: string; @@ -449,7 +544,7 @@ class User { } ``` -Klassen besitzen drei wesentliche Bestandteile: Eigenschaften, Methoden und eine besondere Methode – den Konstruktor. +Klassen bestehen aus mehreren Bausteinen, die wir uns der Reihe nach anschauen. ### Eigenschaften/Propertys @@ -481,23 +576,29 @@ Der Typ `void` sagt aus, dass eine Methode keinen Rückgabewert besitzt. ### Getter und Setter -Mit den Schlüsselwörtern `get` und `set` können wir Methoden verstecken, indem eine Eigenschaft an diese gebunden wird. +Mit den Schlüsselwörtern `get` und `set` können wir Methoden so definieren, dass sie wie Eigenschaften gelesen oder geschrieben werden. +Statt `person.getAge()` schreibt man dann einfach `person.age`. ```typescript class Person { - #birthYear: number; + private birthYear: number; constructor(birthYear: number) { - this.#birthYear = birthYear; + this.birthYear = birthYear; } get age(): number { - return new Date().getFullYear() - this.#birthYear; + return new Date().getFullYear() - this.birthYear; + } + + set age(value: number) { + this.birthYear = new Date().getFullYear() - value; } } const person = new Person(1990); -console.log(person.age); // Berechnet das Alter +console.log(person.age); // Getter: berechnet das Alter +person.age = 25; // Setter: passt das Geburtsjahr an ``` ### Konstruktor @@ -572,6 +673,7 @@ const user = new User('secret'); In TypeScript existiert außerdem der Access Modifier `private`, der die Sichtbarkeit einschränkt. Der Schutz ist allerdings zur Laufzeit nicht garantiert, da TypeScript zu JavaScript umgewandelt wird. Wir empfehlen die moderne JavaScript-Variante mit `#`. +In bestehenden Angular-Projekten ist `private` allerdings noch weit verbreitet und ebenfalls eine gültige Wahl. ## Property Modifiers: `readonly` und `protected` @@ -599,6 +701,7 @@ Für Angular-Projekte empfehlen wir folgende Konventionen: ## Decorators Mit Decorators können wir Klassen, Methoden und Eigenschaften dekorieren und damit Metadaten hinzufügen. +Metadaten sind zusätzliche Informationen über eine Klasse oder Methode — sie beschreiben sie, sind aber nicht Teil ihrer eigentlichen Logik. Man erkennt einen Decorator am `@`-Zeichen zu Beginn des Namens. ```typescript @@ -613,12 +716,16 @@ Angular nutzt dieses Sprachkonzept, um Klassen eine Semantik zu geben: Durch den Decorator `@Component()` wird diese Klasse als Komponente behandelt. Alle Decorators von Angular sind Funktionen, daher darf man die Funktionsklammern bei der Verwendung nicht vergessen. +Angular bringt eine Reihe von Decorators mit, darunter `@Component`, `@Directive`, `@Pipe` und `@Service`. +Sie unterscheiden Klassen nach ihrer Aufgabe innerhalb der Anwendung. + ## Konfiguration Die Konfiguration des TypeScript-Compilers wird in der Datei `tsconfig.json` hinterlegt. -Die wohl wichtigste Einstellung ist das `target`: Diese Option gibt an, in welche Version von JavaScript das Programm transpiliert werden soll. +Eine zentrale Einstellung ist `strict`: Mit `strict: true` werden alle strengen Typprüfungen aktiviert (siehe oben). +Eine weitere wichtige Option ist `target` — sie legt fest, in welche Version von JavaScript der Code transpiliert werden soll. -In einem Angular-Projekt müssen wir uns über die Konfiguration von TypeScript nur wenige Gedanken machen, denn die Einstellungen sind bereits mit sinnvollen Werten vordefiniert. +In einem Angular-Projekt müssen wir uns über die Konfiguration von TypeScript nur wenige Gedanken machen, denn die Einstellungen sind bereits mit sinnvollen Werten vordefiniert — `strict` ist standardmäßig aktiviert. ## Zusammenfassung From 0216116a43cdae418bddb9469322abd933c988be Mon Sep 17 00:00:00 2001 From: Johannes Hoppe Date: Tue, 12 May 2026 14:07:30 +0200 Subject: [PATCH 05/12] =?UTF-8?q?material=20typescript:=20OOP-Abschnitte?= =?UTF-8?q?=20vor=20Arrow=20Functions,=20Konstruktor-R=C3=BCckbezug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Klassen, Private Eigenschaften und Property Modifiers wurden in den Bereich der JS-Basics gezogen und stehen jetzt direkt nach Template-Strings. Damit ist die Klassen-Syntax eingeführt, bevor sie in Arrow Functions (this-Kontext, Counter-Beispiel) verwendet wird. Im Konstruktor-Abschnitt zusätzlicher Rückbezug auf die Transpilier- Kurzschreibweise aus dem Intro, um die Doppelung zu entschärfen. --- material/typescript/README.md | 361 +++++++++++++++++----------------- 1 file changed, 185 insertions(+), 176 deletions(-) diff --git a/material/typescript/README.md b/material/typescript/README.md index 3359ba05..ccbd5715 100644 --- a/material/typescript/README.md +++ b/material/typescript/README.md @@ -1,7 +1,7 @@ --- title: 'Einführung in TypeScript' published: 2026-02-10 -lastModified: 2026-02-05 +lastModified: 2026-04-22 hidden: true --- @@ -14,6 +14,10 @@ TypeScript baut auf JavaScript auf und ergänzt es um ein statisches Typsystem. Wenn du schon Erfahrung mit modernem JavaScript oder TypeScript hast, kannst du diesen Crashkurs überspringen. +## Inhalt + +[[toc]] + ## TypeScript einsetzen TypeScript ist ein Open-Source-Projekt von Microsoft und basiert auf den aktuellen ECMAScript-Standards. @@ -135,6 +139,179 @@ const url = `${apiUrl}/books`; Im Buch arbeiten wir an dieser Stelle bereits mit dem Angular-`HttpClient` und Observables. Für den Moment ist hier nur wichtig, dass der Template-String die Basis-URL mit dem Pfad `/books` zu einer vollständigen URL kombiniert. +## Klassen + +Um eine Klasse zu beschreiben, verwenden wir in JavaScript und TypeScript das Schlüsselwort `class`. +Mit Klassen können einfache Datenobjekte oder auch komplexe objektorientierte Logik abgebildet werden. + +```typescript +class User { + firstname?: string; + lastname: string; + age = 0; + isAdmin: boolean; + + constructor(lastname: string) { + this.lastname = lastname; + this.isAdmin = false; + } +} +``` + +Klassen bestehen aus mehreren Bausteinen, die wir uns der Reihe nach anschauen. + +### Eigenschaften/Propertys + +Eigenschaften (engl. *properties*) erweitern eine Klasseninstanz mit zusätzlichen Informationen. +Propertys können mit den Zugriffsmodifizierern `public`, `private`, `static`, `protected` oder `readonly` versehen werden. +Lässt man die Angabe eines Zugriffsmodifizierers weg, so ist die Eigenschaft immer `public`. + +Ein Property kann als optional deklariert werden, indem wir ein Fragezeichen setzen. +Jedes Property einer Klasse muss immer entweder sofort einen Wert besitzen oder als optional markiert werden. + +### Methoden + +Methoden sind die Funktionen einer Klasse und erweitern die Klasse mit Logik. +Wir können die Methodensignatur präzisieren, indem wir Typen für die Argumente und den Rückgabewert angeben. + +```typescript +class Calculator { + add(a: number, b: number): number { + return a + b; + } + + log(message: string): void { + console.log(message); + } +} +``` + +Der Typ `void` sagt aus, dass eine Methode keinen Rückgabewert besitzt. + +### Getter und Setter + +Mit den Schlüsselwörtern `get` und `set` können wir Methoden so definieren, dass sie wie Eigenschaften gelesen oder geschrieben werden. +Statt `person.getAge()` schreibt man dann einfach `person.age`. + +```typescript +class Person { + private birthYear: number; + + constructor(birthYear: number) { + this.birthYear = birthYear; + } + + get age(): number { + return new Date().getFullYear() - this.birthYear; + } + + set age(value: number) { + this.birthYear = new Date().getFullYear() - value; + } +} + +const person = new Person(1990); +console.log(person.age); // Getter: berechnet das Alter +person.age = 25; // Setter: passt das Geburtsjahr an +``` + +### Konstruktor + +Der Konstruktor ist eine besondere Methode, die bei der Instanziierung einer Klasse aufgerufen wird. +Er muss immer den Namen `constructor()` tragen. + +TypeScript bietet für die Initialisierung von Propertys eine Kurzschreibweise, die wir bereits ganz am Anfang im Transpilier-Beispiel kurz gesehen haben. +Wenn wir in der Methodensignatur des Konstruktors für das Argument einen Zugriffsmodifizierer wie `public` oder `private` verwenden, so wird das zugehörige Property automatisch deklariert und initialisiert. + +```typescript +class User { + constructor( + public firstname: string, + public lastname: string + ) {} +} + +// Entspricht: +class UserLong { + public firstname: string; + public lastname: string; + + constructor(firstname: string, lastname: string) { + this.firstname = firstname; + this.lastname = lastname; + } +} +``` + +### Vererbung + +Die Funktionalität einer Klasse kann auf andere Klassen übertragen werden. +Mit dem Schlüsselwort `extends` kann eine Klasse von einer anderen erben. + +```typescript +class User { + constructor(public name: string) {} +} + +class PowerUser extends User { + constructor(name: string, public permissions: string[]) { + super(name); + } +} +``` + +Mit `super()` kann der Konstruktor der Basisklasse ausgeführt werden. + +## Private Eigenschaften von Klassen + +Mit *Private Class Fields* in JavaScript können wir Datenkapselung in Klassen realisieren. +Ein privates Feld wird durch ein vorangestelltes `#`-Symbol definiert und ist nur innerhalb der Klasse zugänglich. + +```typescript +class User { + #password: string; + + constructor(password: string) { + this.#password = password; + } + + checkPassword(input: string): boolean { + return this.#password === input; + } +} + +const user = new User('secret'); +// user.#password; // Fehler: Zugriff nicht möglich +``` + +In TypeScript existiert außerdem der Access Modifier `private`, der die Sichtbarkeit einschränkt. +Der Schutz ist allerdings zur Laufzeit nicht garantiert, da TypeScript zu JavaScript umgewandelt wird. +Wir empfehlen die moderne JavaScript-Variante mit `#`. +In bestehenden Angular-Projekten ist `private` allerdings noch weit verbreitet und ebenfalls eine gültige Wahl. + +## Property Modifiers: `readonly` und `protected` + +TypeScript stellt uns eine Reihe von *Property Modifiers* zur Verfügung: + +Der Modifier `protected` sorgt für eine eingeschränkte Sichtbarkeit. +Ein Protected Property ist nicht von außen sichtbar, sondern kann nur innerhalb derselben Klasse und in vererbten Kindklassen verwendet werden. +Dazu gehört auch das Template einer Angular-Komponente. + +Mit `readonly` können wir sicherstellen, dass eine Eigenschaft nach der Initialisierung nicht mehr verändert werden kann. + +```typescript +class Config { + readonly apiUrl = 'https://api.example.com'; + protected secret = '12345'; +} +``` + +Für Angular-Projekte empfehlen wir folgende Konventionen: + +- Propertys und Methoden, die nur innerhalb der Klasse verwendet werden, werden als privat markiert. +- Propertys, die im Template einer Komponente genutzt werden, werden mit `protected` gekennzeichnet. +- Propertys, die von Angular verwaltet werden, werden auf `readonly` gesetzt (z. B. `input()`, `output()`, `model()`). + ## Arrow Functions Eine *Arrow-Funktion* ist eine Kurzschreibweise für eine normale `function()` in JavaScript. @@ -525,179 +702,6 @@ const title = signal('Angular'); // Signal const book = signal({ title: 'Angular' }); // Signal ``` -## Klassen - -Um eine Klasse zu beschreiben, verwenden wir in JavaScript und TypeScript das Schlüsselwort `class`. -Mit Klassen können einfache Datenobjekte oder auch komplexe objektorientierte Logik abgebildet werden. - -```typescript -class User { - firstname?: string; - lastname: string; - age = 0; - isAdmin: boolean; - - constructor(lastname: string) { - this.lastname = lastname; - this.isAdmin = false; - } -} -``` - -Klassen bestehen aus mehreren Bausteinen, die wir uns der Reihe nach anschauen. - -### Eigenschaften/Propertys - -Eigenschaften (engl. *properties*) erweitern eine Klasseninstanz mit zusätzlichen Informationen. -Propertys können mit den Zugriffsmodifizierern `public`, `private`, `static`, `protected` oder `readonly` versehen werden. -Lässt man die Angabe eines Zugriffsmodifizierers weg, so ist die Eigenschaft immer `public`. - -Ein Property kann als optional deklariert werden, indem wir ein Fragezeichen setzen. -Jedes Property einer Klasse muss immer entweder sofort einen Wert besitzen oder als optional markiert werden. - -### Methoden - -Methoden sind die Funktionen einer Klasse und erweitern die Klasse mit Logik. -Wir können die Methodensignatur präzisieren, indem wir Typen für die Argumente und den Rückgabewert angeben. - -```typescript -class Calculator { - add(a: number, b: number): number { - return a + b; - } - - log(message: string): void { - console.log(message); - } -} -``` - -Der Typ `void` sagt aus, dass eine Methode keinen Rückgabewert besitzt. - -### Getter und Setter - -Mit den Schlüsselwörtern `get` und `set` können wir Methoden so definieren, dass sie wie Eigenschaften gelesen oder geschrieben werden. -Statt `person.getAge()` schreibt man dann einfach `person.age`. - -```typescript -class Person { - private birthYear: number; - - constructor(birthYear: number) { - this.birthYear = birthYear; - } - - get age(): number { - return new Date().getFullYear() - this.birthYear; - } - - set age(value: number) { - this.birthYear = new Date().getFullYear() - value; - } -} - -const person = new Person(1990); -console.log(person.age); // Getter: berechnet das Alter -person.age = 25; // Setter: passt das Geburtsjahr an -``` - -### Konstruktor - -Der Konstruktor ist eine besondere Methode, die bei der Instanziierung einer Klasse aufgerufen wird. -Er muss immer den Namen `constructor()` tragen. - -TypeScript bietet für die Initialisierung von Propertys eine Kurzschreibweise: -Wenn wir in der Methodensignatur des Konstruktors für das Argument einen Zugriffsmodifizierer wie `public` oder `private` verwenden, so wird das zugehörige Property automatisch deklariert und initialisiert. - -```typescript -class User { - constructor( - public firstname: string, - public lastname: string - ) {} -} - -// Entspricht: -class UserLong { - public firstname: string; - public lastname: string; - - constructor(firstname: string, lastname: string) { - this.firstname = firstname; - this.lastname = lastname; - } -} -``` - -### Vererbung - -Die Funktionalität einer Klasse kann auf andere Klassen übertragen werden. -Mit dem Schlüsselwort `extends` kann eine Klasse von einer anderen erben. - -```typescript -class User { - constructor(public name: string) {} -} - -class PowerUser extends User { - constructor(name: string, public permissions: string[]) { - super(name); - } -} -``` - -Mit `super()` kann der Konstruktor der Basisklasse ausgeführt werden. - -## Private Eigenschaften von Klassen - -Mit *Private Class Fields* in JavaScript können wir Datenkapselung in Klassen realisieren. -Ein privates Feld wird durch ein vorangestelltes `#`-Symbol definiert und ist nur innerhalb der Klasse zugänglich. - -```typescript -class User { - #password: string; - - constructor(password: string) { - this.#password = password; - } - - checkPassword(input: string): boolean { - return this.#password === input; - } -} - -const user = new User('secret'); -// user.#password; // Fehler: Zugriff nicht möglich -``` - -In TypeScript existiert außerdem der Access Modifier `private`, der die Sichtbarkeit einschränkt. -Der Schutz ist allerdings zur Laufzeit nicht garantiert, da TypeScript zu JavaScript umgewandelt wird. -Wir empfehlen die moderne JavaScript-Variante mit `#`. -In bestehenden Angular-Projekten ist `private` allerdings noch weit verbreitet und ebenfalls eine gültige Wahl. - -## Property Modifiers: `readonly` und `protected` - -TypeScript stellt uns eine Reihe von *Property Modifiers* zur Verfügung: - -Der Modifier `protected` sorgt für eine eingeschränkte Sichtbarkeit. -Ein Protected Property ist nicht von außen sichtbar, sondern kann nur innerhalb derselben Klasse und in vererbten Kindklassen verwendet werden. -Dazu gehört auch das Template einer Angular-Komponente. - -Mit `readonly` können wir sicherstellen, dass eine Eigenschaft nach der Initialisierung nicht mehr verändert werden kann. - -```typescript -class Config { - readonly apiUrl = 'https://api.example.com'; - protected secret = '12345'; -} -``` - -Für Angular-Projekte empfehlen wir folgende Konventionen: - -- Propertys und Methoden, die nur innerhalb der Klasse verwendet werden, werden als privat markiert. -- Propertys, die im Template einer Komponente genutzt werden, werden mit `protected` gekennzeichnet. -- Propertys, die von Angular verwaltet werden, werden auf `readonly` gesetzt (z. B. `input()`, `output()`, `model()`). - ## Decorators Mit Decorators können wir Klassen, Methoden und Eigenschaften dekorieren und damit Metadaten hinzufügen. @@ -734,5 +738,10 @@ Mit diesem Crashkurs haben wir die wichtigsten Bausteine von TypeScript kennenge TypeScript ist strenger als JavaScript – und genau das macht die Sprache so wertvoll. Die Typprüfung im Compiler und die Unterstützung durch die IDE helfen uns, Fehler früh zu erkennen und Software wartbar zu entwickeln. -In einem Angular-Projekt ist TypeScript bereits vollständig konfiguriert, sodass wir sofort loslegen können. -Dieser Artikel hat das Fundament gelegt – im Angular-Buch bauen wir darauf auf und wenden die Features praktisch an. +## Fazit + +Wenn du diesen Artikel durchgearbeitet hast, steht der Entwicklung moderner Angular-Anwendungen nichts mehr im Wege. +TypeScript kennt zwar noch viele weitere praktische Konstrukte, aber eine solide Angular-Anwendung benötigt nicht zwingend alle. +Mit dem hier gelernten Werkzeugkasten bist du startklar. + +Viel Spaß mit TypeScript und Angular! From 670ae6594cf05fe838789514371ea72eebd7f120 Mon Sep 17 00:00:00 2001 From: Johannes Hoppe Date: Tue, 12 May 2026 14:15:31 +0200 Subject: [PATCH 06/12] =?UTF-8?q?material=20typescript:=20Basistypen=20nac?= =?UTF-8?q?h=20vorn,=20JS/TS-=C3=9Cberg=C3=A4nge=20entfernt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drei Strukturänderungen, die zusammen den künstlichen JS-Basics/ TS-Typsystem-Split auflösen: 1. Klassen-Beispiele nutzen jeweils semantisch passende Namen (Book, Point, Animal/Dog, BankAccount) statt überall User 2. Basistypen direkt nach Variablen — Typannotationen sind dann von Anfang an klar, wenn sie in Klassen und Funktionen verwendet werden 3. Künstliche JS/TS-Übergangssätze entfernt — die Reihenfolge der Abschnitte spricht für sich --- material/typescript/README.md | 216 +++++++++++++++++----------------- 1 file changed, 108 insertions(+), 108 deletions(-) diff --git a/material/typescript/README.md b/material/typescript/README.md index ccbd5715..e3166ec5 100644 --- a/material/typescript/README.md +++ b/material/typescript/README.md @@ -46,14 +46,14 @@ Ein Beispiel dafür ist die Konstruktor-Kurzschreibweise: ```typescript // TypeScript-Kurzschreibweise im Konstruktor: -class User { - constructor(public name: string) {} +class Book { + constructor(public title: string) {} } // Nach der Transpilierung (JavaScript): -class User { - constructor(name) { - this.name = name; +class Book { + constructor(title) { + this.title = title; } } ``` @@ -110,6 +110,89 @@ for (let i = 0; i < 10; i++) { In älterem JavaScript-Code begegnet uns noch das Schlüsselwort `var`, das vor ECMAScript 2015 die einzige Möglichkeit war, eine Variable zu deklarieren. Im modernen Alltag benötigen wir es nicht mehr — wir verwenden ausschließlich `const` und `let`. +## Die wichtigsten Basistypen + +Die starke Typisierung ermöglicht es, die Datenstrukturen unserer Anwendung präzise zu beschreiben. +So können schon während der Entwicklung hilfreiche Informationen und Warnungen bereitgestellt werden, wenn die API nicht korrekt verwendet wird. + +### Primitive Typen: Zahlen, Zeichenketten und boolesche Werte + +Die wichtigsten primitiven Typen in TypeScript sind `number`, `string` und `boolean`. +Der Typ `number` legt den Wert einer Variable auf eine Ganz- oder Kommazahl fest. +Zeichenketten werden mithilfe des Datentyps `string` definiert. +Wenn eine Variable logische Wahrheitswerte (`true` oder `false`) annehmen soll, verwenden wir den Typ `boolean`. + +Ein Typ wird immer mit einem Doppelpunkt hinter dem Variablennamen deklariert. +Wenn der Typ bereits aus dem Wert eindeutig bestimmbar ist, müssen wir diese Information nicht zwingend notieren. +TypeScript ermittelt den passenden Typ automatisch – man spricht von *Typinferenz*. + +```typescript +let age: number = 30; +let name: string = 'Angular'; +let isActive: boolean = true; + +// Typinferenz: Typ wird automatisch erkannt +let count = 42; // number +let title = 'Buch'; // string +``` + +### Typisierte Arrays + +In JavaScript ist es möglich, ein Array mit verschiedenen Typen zu befüllen. +Mit TypeScript können Arrays typisiert werden, sodass nur Elemente eines festgelegten Typs zulässig sind. + +```typescript +const numbers: number[] = [1, 2, 3]; +const names: string[] = ['Angular', 'React', 'Vue']; + +// Alternative Schreibweise mit Generic +const items: Array = [1, 2, 3]; +``` + +### Beliebige Werte mit `any` und `unknown` + +Eine mit `any` oder `unknown` typisierte Variable kann immer beliebige Werte mit beliebigen Typen annehmen. + +```typescript +let flexible: any = 'text'; +flexible = 42; +flexible = true; +``` + +Diese beiden Basistypen haben jedoch einen wichtigen Unterschied: +Der Wert einer mit `any` typisierten Variable kann zu jeder anderen Variable zugewiesen werden. +`any` wird übrigens auch immer als Standardtyp verwendet, wenn wir eine Variable nicht explizit typisieren und der Typ von TypeScript nicht automatisch ermittelt werden kann. + +Der Typ `unknown` schafft Abhilfe. +Eine solche Variable kann ebenfalls beliebige Werte mit jedem Typ annehmen. +Allerdings kann der Wert einer `unknown`-Variable nur dann einer anderen Variable zugewiesen werden, wenn diese auch den Typ `unknown` oder `any` trägt. +Um den Wert einer mit `unknown` typisierten Variable dennoch zuweisen zu können, müssen wir mithilfe von `typeof` eine Typprüfung vornehmen. + +```typescript +let value: unknown = 'hello'; + +// Typprüfung erforderlich +if (typeof value === 'string') { + const text: string = value; // Jetzt erlaubt +} +``` + +Praktisch solltest du es vermeiden, `any` zu verwenden, denn dieser Typ ist fast immer ein Indiz dafür, dass Unklarheit über die Typisierung herrscht. +Willst du die konkrete Belegung einer Variable absichtlich im Unklaren lassen, ist `unknown` die bessere Wahl. + +In der Praxis begegnet uns `unknown` vor allem in `catch`-Blöcken. +Da ein Fehler von beliebigem Typ sein kann, ist die `error`-Variable standardmäßig als `unknown` typisiert: + +```typescript +try { + // riskante Operation +} catch (error: unknown) { + if (error instanceof Error) { + console.log(error.message); + } +} +``` + ## Template-Strings Mit einem normalen String in einfachen Anführungszeichen ist es nicht möglich, einen Text über mehrere Zeilen anzugeben. @@ -224,21 +307,21 @@ TypeScript bietet für die Initialisierung von Propertys eine Kurzschreibweise, Wenn wir in der Methodensignatur des Konstruktors für das Argument einen Zugriffsmodifizierer wie `public` oder `private` verwenden, so wird das zugehörige Property automatisch deklariert und initialisiert. ```typescript -class User { +class Point { constructor( - public firstname: string, - public lastname: string + public x: number, + public y: number ) {} } // Entspricht: -class UserLong { - public firstname: string; - public lastname: string; +class PointLong { + public x: number; + public y: number; - constructor(firstname: string, lastname: string) { - this.firstname = firstname; - this.lastname = lastname; + constructor(x: number, y: number) { + this.x = x; + this.y = y; } } ``` @@ -249,12 +332,12 @@ Die Funktionalität einer Klasse kann auf andere Klassen übertragen werden. Mit dem Schlüsselwort `extends` kann eine Klasse von einer anderen erben. ```typescript -class User { +class Animal { constructor(public name: string) {} } -class PowerUser extends User { - constructor(name: string, public permissions: string[]) { +class Dog extends Animal { + constructor(name: string, public breed: string) { super(name); } } @@ -268,20 +351,20 @@ Mit *Private Class Fields* in JavaScript können wir Datenkapselung in Klassen r Ein privates Feld wird durch ein vorangestelltes `#`-Symbol definiert und ist nur innerhalb der Klasse zugänglich. ```typescript -class User { - #password: string; +class BankAccount { + #balance: number; - constructor(password: string) { - this.#password = password; + constructor(balance: number) { + this.#balance = balance; } - checkPassword(input: string): boolean { - return this.#password === input; + isEnoughFor(amount: number): boolean { + return this.#balance >= amount; } } -const user = new User('secret'); -// user.#password; // Fehler: Zugriff nicht möglich +const account = new BankAccount(1000); +// account.#balance; // Fehler: Zugriff nicht möglich ``` In TypeScript existiert außerdem der Access Modifier `private`, der die Sichtbarkeit einschränkt. @@ -525,89 +608,6 @@ async function loadData() { } ``` -## Die wichtigsten Basistypen - -Die starke Typisierung ermöglicht es, die Datenstrukturen unserer Anwendung präzise zu beschreiben. -So können schon während der Entwicklung hilfreiche Informationen und Warnungen bereitgestellt werden, wenn die API nicht korrekt verwendet wird. - -### Primitive Typen: Zahlen, Zeichenketten und boolesche Werte - -Die wichtigsten primitiven Typen in TypeScript sind `number`, `string` und `boolean`. -Der Typ `number` legt den Wert einer Variable auf eine Ganz- oder Kommazahl fest. -Zeichenketten werden mithilfe des Datentyps `string` definiert. -Wenn eine Variable logische Wahrheitswerte (`true` oder `false`) annehmen soll, verwenden wir den Typ `boolean`. - -Ein Typ wird immer mit einem Doppelpunkt hinter dem Variablennamen deklariert. -Wenn der Typ bereits aus dem Wert eindeutig bestimmbar ist, müssen wir diese Information nicht zwingend notieren. -TypeScript ermittelt den passenden Typ automatisch – man spricht von *Typinferenz*. - -```typescript -let age: number = 30; -let name: string = 'Angular'; -let isActive: boolean = true; - -// Typinferenz: Typ wird automatisch erkannt -let count = 42; // number -let title = 'Buch'; // string -``` - -### Typisierte Arrays - -In JavaScript ist es möglich, ein Array mit verschiedenen Typen zu befüllen. -Mit TypeScript können Arrays typisiert werden, sodass nur Elemente eines festgelegten Typs zulässig sind. - -```typescript -const numbers: number[] = [1, 2, 3]; -const names: string[] = ['Angular', 'React', 'Vue']; - -// Alternative Schreibweise mit Generic -const items: Array = [1, 2, 3]; -``` - -### Beliebige Werte mit `any` und `unknown` - -Eine mit `any` oder `unknown` typisierte Variable kann immer beliebige Werte mit beliebigen Typen annehmen. - -```typescript -let flexible: any = 'text'; -flexible = 42; -flexible = true; -``` - -Diese beiden Basistypen haben jedoch einen wichtigen Unterschied: -Der Wert einer mit `any` typisierten Variable kann zu jeder anderen Variable zugewiesen werden. -`any` wird übrigens auch immer als Standardtyp verwendet, wenn wir eine Variable nicht explizit typisieren und der Typ von TypeScript nicht automatisch ermittelt werden kann. - -Der Typ `unknown` schafft Abhilfe. -Eine solche Variable kann ebenfalls beliebige Werte mit jedem Typ annehmen. -Allerdings kann der Wert einer `unknown`-Variable nur dann einer anderen Variable zugewiesen werden, wenn diese auch den Typ `unknown` oder `any` trägt. -Um den Wert einer mit `unknown` typisierten Variable dennoch zuweisen zu können, müssen wir mithilfe von `typeof` eine Typprüfung vornehmen. - -```typescript -let value: unknown = 'hello'; - -// Typprüfung erforderlich -if (typeof value === 'string') { - const text: string = value; // Jetzt erlaubt -} -``` - -Praktisch solltest du es vermeiden, `any` zu verwenden, denn dieser Typ ist fast immer ein Indiz dafür, dass Unklarheit über die Typisierung herrscht. -Willst du die konkrete Belegung einer Variable absichtlich im Unklaren lassen, ist `unknown` die bessere Wahl. - -In der Praxis begegnet uns `unknown` vor allem in `catch`-Blöcken. -Da ein Fehler von beliebigem Typ sein kann, ist die `error`-Variable standardmäßig als `unknown` typisiert: - -```typescript -try { - // riskante Operation -} catch (error: unknown) { - if (error instanceof Error) { - console.log(error.message); - } -} -``` - ## Union Types Mit Union Types können wir zusammengesetzte Typen beschreiben: From 326d58a787646330a3f8b8bf4c06ba3f00db0be0 Mon Sep 17 00:00:00 2001 From: Johannes Hoppe Date: Tue, 12 May 2026 14:29:27 +0200 Subject: [PATCH 07/12] =?UTF-8?q?material=20typescript:=20Wording-Fixes=20?= =?UTF-8?q?und=20sachliche=20Pr=C3=A4zisierung?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mehrere kleinere Verbesserungen aus der zweiten Lesung: - Optional Chaining: doppelten user-Setup-Code im Lösungsblock entfernt - any als Standardtyp: präzisiert (gilt nur ohne strict mode, in Angular Compiler-Fehler) - Arrow Functions: "Kurzschreibweise" geschärft, Lambda-Ausdruck-Hinweis entfernt (fachlich nicht korrekt) - Property Modifiers: Config-mit-Klartext-Secret durch Task-Beispiel ersetzt - Eigenschaften: Zugriffsmodifizierer und Modifier sauber getrennt, static jetzt direkt erklärt - Methoden: "erweitern die Klasse mit Logik" -> "enthalten ihre Logik" - Konstruktor: "muss den Namen tragen" -> "heißt immer constructor", Instanziierung -> Erzeugen einer neuen Instanz - unknown: "schafft Abhilfe" -> "Im Gegensatz dazu", plus Klarstellung, dass auch unknown nur ein Notnagel ist - TypeScript einsetzen: "Typisierung geht verloren" -> neutrales "Typinformationen werden entfernt" - Union Types: Konzept der Vereinigung jetzt explizit erklärt - Property Modifiers: holpriger Doppelpunkt durch vollständigen Satz ersetzt --- material/typescript/README.md | 43 +++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/material/typescript/README.md b/material/typescript/README.md index e3166ec5..84a39216 100644 --- a/material/typescript/README.md +++ b/material/typescript/README.md @@ -60,8 +60,8 @@ class Book { Diese Schreibweise schauen wir uns später im Abschnitt zu Klassen genauer an. -Die statische Typisierung geht bei diesem Schritt verloren. -Zur Laufzeit ist das Programm ein reines JavaScript-Programm ohne Typinformationen. +Die Typinformationen werden bei diesem Schritt entfernt. +Zur Laufzeit ist das Programm ein reines JavaScript-Programm ohne Typen. Durch die Typprüfungen bei der Entwicklung und beim Build können viele Fehler aber schon frühzeitig erkannt werden. Die meisten modernen IDEs wie Visual Studio Code oder IntelliJ/WebStorm unterstützen TypeScript nativ und ohne zusätzliche Plug-ins. @@ -161,9 +161,9 @@ flexible = true; Diese beiden Basistypen haben jedoch einen wichtigen Unterschied: Der Wert einer mit `any` typisierten Variable kann zu jeder anderen Variable zugewiesen werden. -`any` wird übrigens auch immer als Standardtyp verwendet, wenn wir eine Variable nicht explizit typisieren und der Typ von TypeScript nicht automatisch ermittelt werden kann. +Ohne strict mode wird `any` außerdem als Standardtyp verwendet, wenn TypeScript den Typ nicht automatisch ermitteln kann. In Angular-Projekten (strict mode aktiv) führt das zu einem Compiler-Fehler. -Der Typ `unknown` schafft Abhilfe. +Im Gegensatz dazu ist `unknown` typsicherer. Eine solche Variable kann ebenfalls beliebige Werte mit jedem Typ annehmen. Allerdings kann der Wert einer `unknown`-Variable nur dann einer anderen Variable zugewiesen werden, wenn diese auch den Typ `unknown` oder `any` trägt. Um den Wert einer mit `unknown` typisierten Variable dennoch zuweisen zu können, müssen wir mithilfe von `typeof` eine Typprüfung vornehmen. @@ -178,7 +178,8 @@ if (typeof value === 'string') { ``` Praktisch solltest du es vermeiden, `any` zu verwenden, denn dieser Typ ist fast immer ein Indiz dafür, dass Unklarheit über die Typisierung herrscht. -Willst du die konkrete Belegung einer Variable absichtlich im Unklaren lassen, ist `unknown` die bessere Wahl. +Aber auch `unknown` ist nur ein Notnagel: Wann immer möglich, sollten wir den konkreten Typ angeben. +Erst wenn das wirklich nicht geht, ist `unknown` die bessere Wahl als `any`. In der Praxis begegnet uns `unknown` vor allem in `catch`-Blöcken. Da ein Fehler von beliebigem Typ sein kann, ist die `error`-Variable standardmäßig als `unknown` typisiert: @@ -246,15 +247,20 @@ Klassen bestehen aus mehreren Bausteinen, die wir uns der Reihe nach anschauen. ### Eigenschaften/Propertys Eigenschaften (engl. *properties*) erweitern eine Klasseninstanz mit zusätzlichen Informationen. -Propertys können mit den Zugriffsmodifizierern `public`, `private`, `static`, `protected` oder `readonly` versehen werden. +Propertys können mit Zugriffsmodifizierern wie `public`, `private` oder `protected` versehen werden, die die Sichtbarkeit einschränken. Lässt man die Angabe eines Zugriffsmodifizierers weg, so ist die Eigenschaft immer `public`. +Daneben gibt es weitere Modifier: + +- `readonly` verhindert Änderungen nach der Initialisierung. Dazu kommen wir noch. +- `static` macht eine Eigenschaft zur Klasse statt zur Instanz zugehörig. Sie wird dann direkt über den Klassennamen aufgerufen, ohne dass eine Instanz erzeugt werden muss. + Ein Property kann als optional deklariert werden, indem wir ein Fragezeichen setzen. Jedes Property einer Klasse muss immer entweder sofort einen Wert besitzen oder als optional markiert werden. ### Methoden -Methoden sind die Funktionen einer Klasse und erweitern die Klasse mit Logik. +Methoden sind die Funktionen einer Klasse und enthalten ihre Logik. Wir können die Methodensignatur präzisieren, indem wir Typen für die Argumente und den Rückgabewert angeben. ```typescript @@ -300,8 +306,8 @@ person.age = 25; // Setter: passt das Geburtsjahr an ### Konstruktor -Der Konstruktor ist eine besondere Methode, die bei der Instanziierung einer Klasse aufgerufen wird. -Er muss immer den Namen `constructor()` tragen. +Der Konstruktor ist eine besondere Methode, die beim Erzeugen einer neuen Instanz aufgerufen wird. +Er heißt immer `constructor`. TypeScript bietet für die Initialisierung von Propertys eine Kurzschreibweise, die wir bereits ganz am Anfang im Transpilier-Beispiel kurz gesehen haben. Wenn wir in der Methodensignatur des Konstruktors für das Argument einen Zugriffsmodifizierer wie `public` oder `private` verwenden, so wird das zugehörige Property automatisch deklariert und initialisiert. @@ -374,7 +380,7 @@ In bestehenden Angular-Projekten ist `private` allerdings noch weit verbreitet u ## Property Modifiers: `readonly` und `protected` -TypeScript stellt uns eine Reihe von *Property Modifiers* zur Verfügung: +TypeScript stellt uns eine Reihe von *Property Modifiers* zur Verfügung, mit denen wir das Verhalten von Eigenschaften präzisieren können. Der Modifier `protected` sorgt für eine eingeschränkte Sichtbarkeit. Ein Protected Property ist nicht von außen sichtbar, sondern kann nur innerhalb derselben Klasse und in vererbten Kindklassen verwendet werden. @@ -383,9 +389,13 @@ Dazu gehört auch das Template einer Angular-Komponente. Mit `readonly` können wir sicherstellen, dass eine Eigenschaft nach der Initialisierung nicht mehr verändert werden kann. ```typescript -class Config { - readonly apiUrl = 'https://api.example.com'; - protected secret = '12345'; +class Task { + readonly id: string; + protected isCompleted = false; + + constructor(id: string) { + this.id = id; + } } ``` @@ -397,8 +407,7 @@ Für Angular-Projekte empfehlen wir folgende Konventionen: ## Arrow Functions -Eine *Arrow-Funktion* ist eine Kurzschreibweise für eine normale `function()` in JavaScript. -Auch die Bezeichnung *Lambda-Ausdruck* ist verbreitet. +Eine *Arrow-Funktion* ist eine kompaktere Schreibweise für eine Funktion in JavaScript — mit einem wichtigen Unterschied beim `this`-Kontext, den wir gleich noch sehen. Die Definition einer anonymen Funktion verkürzt sich damit elegant zu einem Pfeil `=>`. Besitzt die Funktion genau einen Parameter ohne Typ, können die runden Klammern auf der linken Seite weggelassen werden. @@ -564,7 +573,6 @@ Mit Optional Chaining können wir solche Zugriffe absichern. Der `?.`-Operator liefert `undefined`, wenn die linke Seite nicht existiert, statt einen Fehler zu werfen: ```typescript -const user: User = { address: undefined }; const city = user.address?.city; // string | undefined ``` @@ -610,7 +618,8 @@ async function loadData() { ## Union Types -Mit Union Types können wir zusammengesetzte Typen beschreiben: +Ein *Union Type* ist die Vereinigung mehrerer möglicher Typen — eine Variable kann dann einen Wert von einem dieser Typen annehmen. +Mit dem `|`-Operator notieren wir die Alternativen: ```typescript function format(value: string | number): string { From 8a0d32e16870aeec7ae5dbc563732fb1a78e0903 Mon Sep 17 00:00:00 2001 From: Johannes Hoppe Date: Tue, 12 May 2026 14:32:57 +0200 Subject: [PATCH 08/12] =?UTF-8?q?material=20typescript:=20Spiegelstriche?= =?UTF-8?q?=20raus,=20ordentliche=20S=C3=A4tze?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Alle Gedankenstriche (em-dash und en-dash) im Fließtext durch Satztrennungen oder Kommata ersetzt. Auch in den Code-Kommentaren für Konsistenz angepasst. 13 Stellen im Fließtext und 5 in Code-Kommentaren betroffen. --- material/typescript/README.md | 54 +++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/material/typescript/README.md b/material/typescript/README.md index 84a39216..ae3fc283 100644 --- a/material/typescript/README.md +++ b/material/typescript/README.md @@ -76,7 +76,7 @@ Beide wurden mit ECMAScript 2015 eingeführt und unterscheiden sich darin, ob si ### Konstanten mit `const` Variablen, deren Wert sich nach der Initialisierung nicht mehr ändern soll, deklarieren wir mit `const`. -In der Praxis ist das der häufigste Fall — wir empfehlen, eine Variable zunächst immer mit `const` zu deklarieren. +In der Praxis ist das der häufigste Fall. Wir empfehlen, eine Variable zunächst immer mit `const` zu deklarieren. ```typescript const name = 'Angular'; @@ -96,7 +96,7 @@ book.title = 'Angular Buch'; // Das funktioniert! ### Variablen mit `let` Soll sich der Wert einer Variable während des Programmablaufs ändern, deklarieren wir sie mit `let`. -Mit `let` deklarierte Variablen sind blockgebunden: Sie gelten nur innerhalb des Blocks, in dem sie deklariert wurden — typischerweise zwischen geschweiften Klammern wie einer Schleife oder einem `if`-Block. +Mit `let` deklarierte Variablen sind blockgebunden: Sie gelten nur innerhalb des Blocks, in dem sie deklariert wurden, also typischerweise zwischen geschweiften Klammern wie einer Schleife oder einem `if`-Block. ```typescript for (let i = 0; i < 10; i++) { @@ -108,7 +108,7 @@ for (let i = 0; i < 10; i++) { ### Hinweis: das alte `var` In älterem JavaScript-Code begegnet uns noch das Schlüsselwort `var`, das vor ECMAScript 2015 die einzige Möglichkeit war, eine Variable zu deklarieren. -Im modernen Alltag benötigen wir es nicht mehr — wir verwenden ausschließlich `const` und `let`. +Im modernen Alltag benötigen wir es nicht mehr. Wir verwenden ausschließlich `const` und `let`. ## Die wichtigsten Basistypen @@ -124,7 +124,7 @@ Wenn eine Variable logische Wahrheitswerte (`true` oder `false`) annehmen soll, Ein Typ wird immer mit einem Doppelpunkt hinter dem Variablennamen deklariert. Wenn der Typ bereits aus dem Wert eindeutig bestimmbar ist, müssen wir diese Information nicht zwingend notieren. -TypeScript ermittelt den passenden Typ automatisch – man spricht von *Typinferenz*. +TypeScript ermittelt den passenden Typ automatisch. Man spricht von *Typinferenz*. ```typescript let age: number = 30; @@ -227,6 +227,7 @@ Für den Moment ist hier nur wichtig, dass der Template-String die Basis-URL mit Um eine Klasse zu beschreiben, verwenden wir in JavaScript und TypeScript das Schlüsselwort `class`. Mit Klassen können einfache Datenobjekte oder auch komplexe objektorientierte Logik abgebildet werden. +Ein konkretes Objekt einer Klasse nennen wir eine *Instanz*. ```typescript class User { @@ -407,7 +408,8 @@ Für Angular-Projekte empfehlen wir folgende Konventionen: ## Arrow Functions -Eine *Arrow-Funktion* ist eine kompaktere Schreibweise für eine Funktion in JavaScript — mit einem wichtigen Unterschied beim `this`-Kontext, den wir gleich noch sehen. +Eine *Arrow-Funktion* ist eine kompaktere Schreibweise für eine Funktion in JavaScript. +Sie hat allerdings einen wichtigen Unterschied beim `this`-Kontext, den wir gleich noch sehen. Die Definition einer anonymen Funktion verkürzt sich damit elegant zu einem Pfeil `=>`. Besitzt die Funktion genau einen Parameter ohne Typ, können die runden Klammern auf der linken Seite weggelassen werden. @@ -432,14 +434,15 @@ const even1 = numbers.filter(function(n) { return n % 2 === 0; }); -// Arrow-Funktion – wesentlich kompakter +// Arrow-Funktion, wesentlich kompakter const even2 = numbers.filter(n => n % 2 === 0); ``` ### Der `this`-Kontext In JavaScript bezieht sich `this` innerhalb einer Methode normalerweise auf die Klasseninstanz, zu der die Methode gehört. -Bei einer klassischen Funktion mit dem Schlüsselwort `function` ändert sich dieser Bezug allerdings je nach Aufrufkontext — und das führt schnell zu Fehlern. +Bei einer klassischen Funktion mit dem Schlüsselwort `function` ändert sich dieser Bezug allerdings je nach Aufrufkontext. +Das führt schnell zu Fehlern. Im folgenden Beispiel kennt die Funktion in `setTimeout` die Klasseninstanz nicht mehr: @@ -482,7 +485,7 @@ const book = { title: 'Angular', year: 2023 }; const copy = book; copy.year = 2024; -console.log(book.year); // 2024 – auch das Original wurde geändert! +console.log(book.year); // 2024, auch das Original wurde geändert! ``` Um gut wartbaren Code zu erhalten, dürfen wir niemals die Werte eines Objekts oder Arrays direkt verändern. @@ -512,13 +515,25 @@ Mit der Spread-Syntax können wir Objekte klonen und dabei Eigenschaften übersc const book = { title: 'Angular', year: 2023 }; const copy = { ...book, year: 2026 }; -console.log(book.year); // 2023 – Original unverändert -console.log(copy.year); // 2026 – Kopie mit neuem Wert +console.log(book.year); // 2023, Original unverändert +console.log(copy.year); // 2026, Kopie mit neuem Wert ``` Bitte beachte, dass diese Idee nur für *Plain Objects* funktioniert und nur eine flache Kopie (*Shallow Copy*) erzeugt. -Tiefere Zweige eines Objekts müssen einzeln geklont werden. -Wird diese Aufgabe zu kompliziert, können wir die native Funktion `structuredClone()` verwenden, die eine *Deep Copy* erzeugt. +Verschachtelte Objekte werden zwischen Original und Kopie geteilt: + +```typescript +const book = { + title: 'Angular', + author: { name: 'Hoppe' } +}; + +const copy = { ...book }; +copy.author.name = 'Mustermann'; +console.log(book.author.name); // 'Mustermann', das verschachtelte Objekt wurde geteilt! +``` + +Wird das zum Problem, können wir die native Funktion `structuredClone()` verwenden, die eine *Deep Copy* erzeugt und damit auch alle verschachtelten Objekte kopiert. ### Array-Elemente kopieren @@ -557,7 +572,8 @@ console.log(sum(1, 2, 3, 4)); // 10 ## Optional Chaining -Wenn ein verschachteltes Objekt eine optionale Eigenschaft hat, könnte der Zugriff darauf fehlschlagen — TypeScript warnt uns davor: +Wenn ein verschachteltes Objekt eine optionale Eigenschaft hat, könnte der Zugriff darauf fehlschlagen. +TypeScript warnt uns davor: ```typescript type User = { @@ -593,7 +609,7 @@ console.log(zero ?? 'fallback'); // 0 ## Promises und `async`/`await` -Manche Vorgänge brauchen Zeit — zum Beispiel ein Netzwerk-Aufruf an einen Server. +Manche Vorgänge brauchen Zeit, zum Beispiel ein Netzwerk-Aufruf an einen Server. JavaScript wartet darauf nicht, sondern führt den restlichen Code weiter aus und meldet sich später mit dem Ergebnis. Solche Vorgänge nennt man *asynchron*. @@ -618,7 +634,7 @@ async function loadData() { ## Union Types -Ein *Union Type* ist die Vereinigung mehrerer möglicher Typen — eine Variable kann dann einen Wert von einem dieser Typen annehmen. +Ein *Union Type* ist die Vereinigung mehrerer möglicher Typen. Eine Variable kann dann einen Wert von einem dieser Typen annehmen. Mit dem `|`-Operator notieren wir die Alternativen: ```typescript @@ -714,7 +730,7 @@ const book = signal({ title: 'Angular' }); // Signal ## Decorators Mit Decorators können wir Klassen, Methoden und Eigenschaften dekorieren und damit Metadaten hinzufügen. -Metadaten sind zusätzliche Informationen über eine Klasse oder Methode — sie beschreiben sie, sind aber nicht Teil ihrer eigentlichen Logik. +Metadaten sind zusätzliche Informationen über eine Klasse oder Methode. Sie beschreiben sie, sind aber nicht Teil ihrer eigentlichen Logik. Man erkennt einen Decorator am `@`-Zeichen zu Beginn des Namens. ```typescript @@ -736,15 +752,15 @@ Sie unterscheiden Klassen nach ihrer Aufgabe innerhalb der Anwendung. Die Konfiguration des TypeScript-Compilers wird in der Datei `tsconfig.json` hinterlegt. Eine zentrale Einstellung ist `strict`: Mit `strict: true` werden alle strengen Typprüfungen aktiviert (siehe oben). -Eine weitere wichtige Option ist `target` — sie legt fest, in welche Version von JavaScript der Code transpiliert werden soll. +Eine weitere wichtige Option ist `target`. Sie legt fest, in welche Version von JavaScript der Code transpiliert werden soll. -In einem Angular-Projekt müssen wir uns über die Konfiguration von TypeScript nur wenige Gedanken machen, denn die Einstellungen sind bereits mit sinnvollen Werten vordefiniert — `strict` ist standardmäßig aktiviert. +In einem Angular-Projekt müssen wir uns über die Konfiguration von TypeScript nur wenige Gedanken machen, denn die Einstellungen sind bereits mit sinnvollen Werten vordefiniert. `strict` ist standardmäßig aktiviert. ## Zusammenfassung Mit diesem Crashkurs haben wir die wichtigsten Bausteine von TypeScript kennengelernt: moderne Sprachfeatures aus JavaScript wie `const`/`let`, Arrow Functions und die Spread-Syntax, dazu das Typsystem von TypeScript mit Interfaces, Union Types und Generics, und schließlich die Objektorientierung mit Klassen und Property Modifiers. -TypeScript ist strenger als JavaScript – und genau das macht die Sprache so wertvoll. +TypeScript ist strenger als JavaScript, und genau das macht die Sprache so wertvoll. Die Typprüfung im Compiler und die Unterstützung durch die IDE helfen uns, Fehler früh zu erkennen und Software wartbar zu entwickeln. ## Fazit From 42bd189c92d073e69ce3adc775f5acf9973e3ba2 Mon Sep 17 00:00:00 2001 From: Johannes Hoppe Date: Tue, 12 May 2026 14:47:13 +0200 Subject: [PATCH 09/12] =?UTF-8?q?material=20typescript:=20export=20class?= =?UTF-8?q?=20im=20Decorator-Beispiel=20erg=C3=A4nzt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- material/typescript/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/material/typescript/README.md b/material/typescript/README.md index ae3fc283..02bcd709 100644 --- a/material/typescript/README.md +++ b/material/typescript/README.md @@ -738,7 +738,7 @@ Man erkennt einen Decorator am `@`-Zeichen zu Beginn des Namens. selector: 'app-root', template: '

Hello

' }) -class AppComponent {} +export class AppComponent {} ``` Angular nutzt dieses Sprachkonzept, um Klassen eine Semantik zu geben: From 7a6360e09c1c09e17eefab3ff112dcb2730f52c4 Mon Sep 17 00:00:00 2001 From: Johannes Hoppe Date: Tue, 12 May 2026 14:49:22 +0200 Subject: [PATCH 10/12] material typescript: Wording-Feinschliff --- material/typescript/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/material/typescript/README.md b/material/typescript/README.md index 02bcd709..03e5f2c1 100644 --- a/material/typescript/README.md +++ b/material/typescript/README.md @@ -60,7 +60,6 @@ class Book { Diese Schreibweise schauen wir uns später im Abschnitt zu Klassen genauer an. -Die Typinformationen werden bei diesem Schritt entfernt. Zur Laufzeit ist das Programm ein reines JavaScript-Programm ohne Typen. Durch die Typprüfungen bei der Entwicklung und beim Build können viele Fehler aber schon frühzeitig erkannt werden. @@ -248,7 +247,7 @@ Klassen bestehen aus mehreren Bausteinen, die wir uns der Reihe nach anschauen. ### Eigenschaften/Propertys Eigenschaften (engl. *properties*) erweitern eine Klasseninstanz mit zusätzlichen Informationen. -Propertys können mit Zugriffsmodifizierern wie `public`, `private` oder `protected` versehen werden, die die Sichtbarkeit einschränken. +Propertys können mit Zugriffsmodifizierern wie `public`, `private` oder `protected` versehen werden, um die Sichtbarkeit zu steuern. Lässt man die Angabe eines Zugriffsmodifizierers weg, so ist die Eigenschaft immer `public`. Daneben gibt es weitere Modifier: @@ -257,7 +256,7 @@ Daneben gibt es weitere Modifier: - `static` macht eine Eigenschaft zur Klasse statt zur Instanz zugehörig. Sie wird dann direkt über den Klassennamen aufgerufen, ohne dass eine Instanz erzeugt werden muss. Ein Property kann als optional deklariert werden, indem wir ein Fragezeichen setzen. -Jedes Property einer Klasse muss immer entweder sofort einen Wert besitzen oder als optional markiert werden. +Jedes Property einer Klasse muss entweder sofort einen Wert besitzen, im Konstruktor zugewiesen werden oder als optional markiert sein. ### Methoden @@ -659,7 +658,8 @@ currentStatus = 'success'; // OK ## Interfaces Um die Typisierung in unserem Programmcode konsequent umzusetzen, stellt TypeScript sogenannte *Interfaces* bereit. -Interfaces dienen dazu, die typisierte Struktur eines Objekts zu definieren, nicht jedoch die Werte. +Interfaces beschreiben, welche Eigenschaften ein Objekt haben muss und welchen Typ diese Eigenschaften haben. +Konkrete Werte legen sie nicht fest. Optionale Eigenschaften werden durch ein Fragezeichen-Symbol gekennzeichnet. ```typescript From c1c06d9af04124b54526b568ebe0641a3f449577 Mon Sep 17 00:00:00 2001 From: Johannes Hoppe Date: Tue, 12 May 2026 14:51:07 +0200 Subject: [PATCH 11/12] material typescript: hidden raus --- material/typescript/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/material/typescript/README.md b/material/typescript/README.md index 03e5f2c1..4cdaa46d 100644 --- a/material/typescript/README.md +++ b/material/typescript/README.md @@ -2,7 +2,6 @@ title: 'Einführung in TypeScript' published: 2026-02-10 lastModified: 2026-04-22 -hidden: true --- Für die Entwicklung mit Angular verwenden wir die Programmiersprache **TypeScript**. From 2f99e78dac0c58feeef40349d36e2f711623130c Mon Sep 17 00:00:00 2001 From: Ferdinand Malcher Date: Wed, 13 May 2026 09:48:09 +0200 Subject: [PATCH 12/12] Leerzeichen --- material/typescript/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/material/typescript/README.md b/material/typescript/README.md index 4cdaa46d..3322d3f4 100644 --- a/material/typescript/README.md +++ b/material/typescript/README.md @@ -688,7 +688,7 @@ interface Printable { class Document implements Printable { print(): void { - console.log('Printing...'); + console.log('Printing ...'); } } ```