diff --git a/05_typescript/docs/in-operator-vs-type-predicate.pdf b/05_typescript/docs/in-operator-vs-type-predicate.pdf
new file mode 100644
index 0000000..57c2923
Binary files /dev/null and b/05_typescript/docs/in-operator-vs-type-predicate.pdf differ
diff --git a/05_typescript/unterricht/tag31/01_ts-narrowing-guards/assets/js/04_in-operator.js b/05_typescript/unterricht/tag31/01_ts-narrowing-guards/assets/js/04_in-operator.js
index 1cd87df..873a7ab 100644
--- a/05_typescript/unterricht/tag31/01_ts-narrowing-guards/assets/js/04_in-operator.js
+++ b/05_typescript/unterricht/tag31/01_ts-narrowing-guards/assets/js/04_in-operator.js
@@ -1,5 +1,30 @@
(() => {
// src/04_in-operator.ts
{
+ const operate = (vehicle) => {
+ if ("drive" in vehicle) {
+ vehicle.drive();
+ } else {
+ vehicle.sail();
+ }
+ };
+ const car = { drive: () => console.log("I'm driving") };
+ const sealander = { sail: () => console.log("I'm sailing") };
+ operate(car);
+ operate(sealander);
+ const move = (entity) => {
+ if ("walk" in entity) {
+ entity.walk?.();
+ } else if ("fly" in entity && entity.fly) {
+ entity.fly();
+ } else if ("bark" in entity) {
+ entity.bark();
+ }
+ };
+ const dog = { bark: () => console.log("Woof") };
+ const bird = { fly: () => console.log("Flying") };
+ const robot = { walk: () => console.log("Walking") };
+ const optimus = { fly: () => console.log("Flying Robo") };
+ move(robot);
}
})();
diff --git a/05_typescript/unterricht/tag31/01_ts-narrowing-guards/assets/js/05_kontrollflussanalyse.js b/05_typescript/unterricht/tag31/01_ts-narrowing-guards/assets/js/05_kontrollflussanalyse.js
new file mode 100644
index 0000000..9428cf2
--- /dev/null
+++ b/05_typescript/unterricht/tag31/01_ts-narrowing-guards/assets/js/05_kontrollflussanalyse.js
@@ -0,0 +1,37 @@
+(() => {
+ // src/05_kontrollflussanalyse.ts
+ {
+ const processData = (data) => {
+ if (typeof data === "boolean") {
+ console.log("Boolean value:", data);
+ } else if (typeof data === "string") {
+ console.log("String value:", data.toUpperCase());
+ } else {
+ console.log("Number value:", data.toFixed(2));
+ }
+ };
+ processData(true);
+ processData("hello");
+ processData(123);
+ const checkValue = (value) => {
+ if (typeof value === "string") {
+ return `String: ${value}`;
+ }
+ return `Number: ${value.toFixed(2)}`;
+ };
+ console.log(checkValue("hello"));
+ console.log(checkValue(123));
+ const describeInput = (input) => {
+ if (input === null) {
+ return "No input provided.";
+ }
+ if (typeof input === "string") {
+ return `String input: ${input.toUpperCase()}`;
+ }
+ return `Number input: ${input.toFixed(2)}`;
+ };
+ console.log(describeInput("hello"));
+ console.log(describeInput(123));
+ console.log(describeInput(null));
+ }
+})();
diff --git a/05_typescript/unterricht/tag31/01_ts-narrowing-guards/assets/js/06_type-predicate.js b/05_typescript/unterricht/tag31/01_ts-narrowing-guards/assets/js/06_type-predicate.js
new file mode 100644
index 0000000..f995964
--- /dev/null
+++ b/05_typescript/unterricht/tag31/01_ts-narrowing-guards/assets/js/06_type-predicate.js
@@ -0,0 +1,28 @@
+(() => {
+ // src/06_type-predicate.ts
+ {
+ const isFish = (pet2) => {
+ return Object.hasOwn(pet2, "swim");
+ };
+ const handlePet = (pet2) => {
+ if (isFish(pet2)) {
+ pet2.swim();
+ } else {
+ pet2.fly();
+ }
+ };
+ const pet = { swim: () => console.log("Is swimming") };
+ const amsel = { fly: () => console.log("Is flying") };
+ handlePet(pet);
+ handlePet(amsel);
+ const zoo = [
+ { swim: () => console.log("Fish swimming") },
+ { fly: () => console.log("Bird flying") },
+ { fly: () => console.log("Another Bird flying") },
+ { fly: () => console.log("Bird3 flying") },
+ { swim: () => console.log("Another Fish flying") }
+ ];
+ const fishes = zoo.filter((pet2) => isFish(pet2));
+ fishes.forEach((fish) => fish.swim());
+ }
+})();
diff --git a/05_typescript/unterricht/tag31/01_ts-narrowing-guards/index.html b/05_typescript/unterricht/tag31/01_ts-narrowing-guards/index.html
index e99f67d..d28f648 100644
--- a/05_typescript/unterricht/tag31/01_ts-narrowing-guards/index.html
+++ b/05_typescript/unterricht/tag31/01_ts-narrowing-guards/index.html
@@ -6,9 +6,12 @@
TypeScript - Grundlagen
-
+
+
diff --git a/05_typescript/unterricht/tag31/01_ts-narrowing-guards/src/04_in-operator.md b/05_typescript/unterricht/tag31/01_ts-narrowing-guards/src/04_in-operator.md
new file mode 100644
index 0000000..71a8048
--- /dev/null
+++ b/05_typescript/unterricht/tag31/01_ts-narrowing-guards/src/04_in-operator.md
@@ -0,0 +1,86 @@
+## `in`-Operator vs Type Predicate
+
+Der `in`-Operator fungiert in TypeScript als **eingebauter Type Guard** und bewirkt ein automatisches **Type Narrowing**, verhält sich im Detail jedoch flexibler und nachsichtiger als ein handgeschriebenes Type Predicate (`pet is Fish`).
+
+---
+
+### Gemeinsamkeit
+
+Beide Mechanismen verengen eine Union:
+
+```typescript
+// Variante A: Direkt mit `in`
+if ('swim' in pet) {
+ pet.swim(); // TS verengt pet zu Fish
+} else {
+ pet.fly(); // TS verengt pet zu Bird
+}
+
+// Variante B: Mit Type Predicate
+if (isFish(pet)) {
+ pet.swim(); // TS verengt pet zu Fish
+} else {
+ pet.fly(); // TS verengt pet zu Bird
+}
+
+```
+
+In einfachen Unions mit distinkten Properties ist der praktische Effekt im `if`/`else`-Block identisch.
+
+---
+
+### Wesentliche Unterschiede
+
+| Kriterium | `in`-Operator | Eigene Predicate-Funktion (`pet is Fish`) |
+| --- | --- | --- |
+| **Prüflogik** | Prüft ausschließlich die **Existenz des Keys** (auch in der Prototypenkette). | Beliebige Laufzeit-Validierung (Werte, Typen, verschachtelte Felder, Regex etc.). |
+| **Wiederverwendbarkeit** | Nur inline an der Stelle der Prüfung. | Modular exportierbar und z. B. in `.filter(isFish)` nutzbar. |
+| **Typsicherheit** | **Vollständig vom Compiler verifiziert.** TS schaut sich die Typdefinitionen an. | **Verhält sich wie ein Type Cast (`as`).** TS vertraut blind dem Rückgabewert `pet is Fish`, selbst wenn die Laufzeitprüfung fehlerhaft ist. |
+| **Umgang mit unbekannten Typen** | Verengt `object` oder `unknown` zu `Record<"swim", unknown> & object`. | Verengt direkt auf den Zieltyp `Fish`. |
+
+---
+
+### 1. Type Predicates sind "Vertrauenssache" (Compiler prüft nicht den Inhalt)
+
+Wenn du ein Prädikat schreibst:
+
+```typescript
+const isFish = (pet: Fish | Bird): pet is Fish => {
+ return true; // Bug!
+};
+
+if (isFish(pet)) {
+ pet.swim(); // TS meckert nicht, crasht aber zur Laufzeit
+}
+
+```
+
+TypeScript überprüft nicht, ob die Logik im Body von `isFish` tatsächlich beweist, dass es sich um einen Fisch handelt. Beim `in`-Operator hingegen wertet TypeScript die tatsächlichen Eigenschaften der beteiligten Typen aus.
+
+---
+
+### 2. Wiederverwendbarkeit in Array-Methoden
+
+Ein großer Vorteil des Type Predicates ist die Verwendung in Higher-Order Functions:
+
+```typescript
+const pets: (Fish | Bird)[] = [/* ... */];
+
+// Funktioniert nur mit Predicate:
+const fishes: Fish[] = pets.filter(isFish);
+
+// Funktioniert nicht direkt mit 'in':
+// pets.filter(pet => 'swim' in pet) liefert weiterhin (Fish | Bird)[]
+
+```
+
+---
+
+### 3. Prototypenkette vs. Own Properties
+
+In deinem Code-Kommentar steht:
+
+* `Object.hasOwn(pet, 'swim')` bzw. `pet.hasOwnProperty('swim')` prüft nur **eigene Eigenschaften**.
+* `'swim' in pet` prüft auch Methoden, die auf dem **Prototyp** definiert sind (z. B. wenn `Fish` eine Klasse ist).
+
+Wenn `Fish` als Klasse deklariert ist, liegt `swim()` auf `Fish.prototype`. In diesem Fall würde `Object.hasOwn(pet, 'swim')` zur Laufzeit `false` liefern, während `'swim' in pet` korrekt `true` ergibt.
\ No newline at end of file
diff --git a/05_typescript/unterricht/tag31/01_ts-narrowing-guards/src/04_in-operator.ts b/05_typescript/unterricht/tag31/01_ts-narrowing-guards/src/04_in-operator.ts
index 92a7231..45bfd6f 100644
--- a/05_typescript/unterricht/tag31/01_ts-narrowing-guards/src/04_in-operator.ts
+++ b/05_typescript/unterricht/tag31/01_ts-narrowing-guards/src/04_in-operator.ts
@@ -1,5 +1,55 @@
// In Operator - wird verwendet, um zu bestimmen, ob ein Objekt ein bestimmtes Property hat.
{
- type Car = { drive: () => void };
- type Boat = { sail: () => void };
+ type Car = {
+ drive: () => void;
+ // drive():void
+ };
+ type Boat = {
+ sail: () => void;
+ // sail(): void
+ };
+
+ const operate = (vehicle: Car | Boat) => {
+ // Property 'drive' does not exist on type 'Boat'.
+ // vehicle.drive(); // Property 'drive' does not exist on type 'Car | Boat'.
+
+ // Object.hasOwn() führt kein narrowing aus | grenzt nicht den Datentyp ein
+ // if (Object.hasOwn(vehicle, 'drive')) {
+ if ('drive' in vehicle) {
+ vehicle.drive();
+ } else {
+ vehicle.sail();
+ }
+ };
+
+ const car: Car = { drive: () => console.log("I'm driving") };
+ const sealander: Boat = { sail: () => console.log("I'm sailing") };
+
+ operate(car);
+ operate(sealander);
+
+ // -------
+ type Dog = { bark: () => void };
+ type Bird = { fly: () => void };
+ type Robot = { walk?: () => void; fly?: () => void | undefined };
+
+ const move = (entity: Dog | Bird | Robot) => {
+ if ('walk' in entity) {
+ entity.walk?.();
+ } else if ('fly' in entity && entity.fly) {
+ entity.fly();
+ } else if ('bark' in entity) {
+ entity.bark();
+ }
+ };
+
+ const dog: Dog = { bark: () => console.log('Woof') };
+ const bird: Bird = { fly: () => console.log('Flying') };
+ const robot: Robot = { walk: () => console.log('Walking') };
+ const optimus: Robot = { fly: () => console.log('Flying Robo') };
+
+ // move(dog);
+ // move(bird);
+ move(robot);
+ // move(optimus);
}
diff --git a/05_typescript/unterricht/tag31/01_ts-narrowing-guards/src/05_kontrollflussanalyse.ts b/05_typescript/unterricht/tag31/01_ts-narrowing-guards/src/05_kontrollflussanalyse.ts
new file mode 100644
index 0000000..66b4791
--- /dev/null
+++ b/05_typescript/unterricht/tag31/01_ts-narrowing-guards/src/05_kontrollflussanalyse.ts
@@ -0,0 +1,52 @@
+// Kontrollflussanalyse
+/*
+In TypeScript wird der Kontrollfluss analysiert, um zu bestimmen, welche Typen eine Variable haben kann. Dies wird auch als "Type Narrowing" bezeichnet.
+Type Narrowing wird verwendet, um den Typ einer Variablen zu beschränken, wenn bestimmte Bedingungen erfüllt sind.
+*/
+
+{
+ const processData = (data: string | number | boolean): void => {
+ if (typeof data === 'boolean') {
+ console.log('Boolean value:', data);
+ } else if (typeof data === 'string') {
+ console.log('String value:', data.toUpperCase());
+ } else {
+ console.log('Number value:', data.toFixed(2));
+ }
+ };
+
+ processData(true);
+ processData('hello');
+ processData(123);
+
+ // Kontrollfluss mit Early Returns
+
+ const checkValue = (value: string | number): string => {
+ // Guard
+ if (typeof value === 'string') {
+ return `String: ${value}`; // early return bei Narrowing
+ }
+
+ return `Number: ${value.toFixed(2)}`;
+ };
+
+ console.log(checkValue('hello'));
+ console.log(checkValue(123));
+
+ // ---
+ const describeInput = (input: string | number | null): string => {
+ if (input === null) {
+ return 'No input provided.';
+ }
+
+ if (typeof input === 'string') {
+ return `String input: ${input.toUpperCase()}`;
+ }
+
+ return `Number input: ${input.toFixed(2)}`;
+ };
+
+ console.log(describeInput('hello'));
+ console.log(describeInput(123));
+ console.log(describeInput(null));
+}
diff --git a/05_typescript/unterricht/tag31/01_ts-narrowing-guards/src/06_type-predicate.ts b/05_typescript/unterricht/tag31/01_ts-narrowing-guards/src/06_type-predicate.ts
new file mode 100644
index 0000000..0740dd7
--- /dev/null
+++ b/05_typescript/unterricht/tag31/01_ts-narrowing-guards/src/06_type-predicate.ts
@@ -0,0 +1,57 @@
+// Type Predicate
+/*
+Ein Type Predicate ist eine besondere Art von Funktion, die einen booleschen Wert zurückgibt, der angibt, ob eine Variable einem bestimmten Typ entspricht.
+
+WICHTIG: "Außerdem grenzt es den Typ der Variablen auf der Grundlage des Ergebnisses der Prüfung ein."
+*/
+
+{
+ type Fish = { swim: () => void };
+ type Bird = { fly: () => void };
+
+ // "pet is Fish" <- Type predicate
+ const isFish = (pet: Fish | Bird): pet is Fish => {
+ return Object.hasOwn(pet, 'swim');
+ // return pet.hasOwnProperty('swim');
+ // return typeof (pet as Fish).swim !== 'undefined';
+
+ // return 'swim' in pet; // in Operator führt Type-Narrowing durch
+ };
+
+ const handlePet = (pet: Fish | Bird) => {
+ // Property 'swim' does not exist on type 'Fish | Bird'.
+ // Property 'swim' does not exist on type 'Bird'.
+ // pet.swim();
+
+ // if ('swim' in pet) {
+ if (isFish(pet)) {
+ pet.swim(); // TypeScript knows pet is a Fish Object
+ } else {
+ pet.fly(); // TypeScript knows pet is a Bird Object
+ }
+ };
+
+ const pet: Fish | Bird = { swim: () => console.log('Is swimming') };
+ const amsel: Bird = { fly: () => console.log('Is flying') };
+
+ handlePet(pet);
+ handlePet(amsel);
+
+ //------
+ // const zoo: (Fish | Bird)[] = [
+ // { swim: () => console.log('Fish swimming') },
+ // { fly: () => console.log('Bird flying') },
+ // { swim: () => console.log('Another Fish flying') },
+ // ];
+ const zoo: Array = [
+ { swim: () => console.log('Fish swimming') },
+ { fly: () => console.log('Bird flying') },
+ { fly: () => console.log('Another Bird flying') },
+ { fly: () => console.log('Bird3 flying') },
+ { swim: () => console.log('Another Fish flying') },
+ ];
+
+ const fishes = zoo.filter((pet) => isFish(pet));
+
+ fishes.forEach((fish) => fish.swim());
+}