Geschichte, Best Practice, einfaches Beispiel, Edge Case Beispiel, Proof of Concept Beispiel

Geschichte

Fortran gehörte 1954 zu den ersten Programmiersprachen mit Funktionen. Zu dieser Zeit allerdings noch ohne spezifische Return-Typen. 1958 trug die Programmiersprache Algol zur strukturierten Programmierung bei, die wiederum moderne Funktionen beeinflusste. Genauer gesagt war Algol eine der ersten, die Funktionen mit Rückgabewerten einführte.

1972 haben die Programmierer der weit verbreiteten Programmiersprache C die explizite Return-Type für Funktionen einführten. Sie verbesserten damit die Programmstabilität und Sicherheit erheblich. Durch die Bekanntheit von C popularisierte, die Idee, Return-Typen streng zu definieren und wurde von vielen späteren Programmiersprachen übernommen und verfeinert, darunter C++, Java und schließlich TypeScript.

Mit der Entwicklung von TypeScript wurde die Notwendigkeit und der Nutzen von Return-Typen noch deutlicher. TypeScript bietet die Möglichkeit, Funktionen mit klar definierten Rückgabewerten zu versehen, was zu einer besseren Fehlererkennung zur Entwicklungszeit und zu einem insgesamt robusteren Code führt.

Best Practice

Return-Typen sind genau wie Deklarations-Typen von Variablen ein wichtiger Bestandteil für die Clean-Code (Nachhaltigkeit) Programmierung.

Heutzutage bieten Programmiersprachen gute Fehlermeldungen. Wenn man sich nicht an bestimmte Programmierregeln hält, können Fehlermeldungen entstehen, die schwer verständlich sind oder eine lange Diagnose erfordern.

Daher ist es ratsam, nach dem Erlernen von JavaScript auf TypeScript umzusteigen.

// TypeScript-Konfiguration (tsconfig.json) muss "noImplicitAny": true enthalten

function fetchData(id: number) {
    if (id > 0) {
        return { data: "Some data", status: 200 };
    }
}

function processData(id: number) {
    const result = fetchData(id);

    if (result.status === 200) { 
        console.log(result.data.toUpperCase());
    } else {
        console.log("No data found");
    }
}

function main() {
    processData(1);  // Funktioniert wie erwartet
    processData(-1); // Führt zu einem schwer erkennbaren Fehler
}

main();

In diesem Beispiel bekommen wir bei processData(-1) den Fehler undefined. Wo ist der Fehler und wie könnte man ihn korrigieren? Was könnten wir unabhängig von dem eigentlichen Fehler noch verbessern? Manchmal ist es so, als würde man die Rechtschreibung eines Artikels prüfen, nicht wahr?

Return-Typen sind somit wichtig, weil sie die Typensicherheit gewährleisten, Lesbarkeit verbessern, die Fehleranfälligkeit reduzieren, Klarheit und Absicht ausdrücken und die Wartbarkeit verbessern.

Einfache Beispiele

Hier sind die verschiedenen Rückgabewerte (Return-Typen) von Funktionen in TypeScript:

  1. Primitive Typen:
    • string: Gibt eine Zeichenkette zurück.
      function greet(): string {
          return "Hello, world!";
      }
    • number: Gibt eine Zahl zurück.
      function getAge(): number {
          return 30;
      }
      
    • boolean: Gibt einen Wahrheitswert (true oder false) zurück.
      function isAdult(age: number): boolean {
          return age >= 18;
      }
  2. void:
    • Gibt keinen Wert zurück.
      function logMessage(message: string): void {
          console.log(message);
      }

      Wenn du Funktionen schreibst, die keinen Rückgabewert benötigen, solltest du void verwenden. Funktionen ohne Rückgabewert sind für den Folge-Algorithmus nicht von Bedeutung.

  3. Array:
    • Gibt ein Array zurück, z. B. ein Array von Zahlen.
      function getNumbers(): number[] {
          return [1, 2, 3];
      }
      
  4. Object:
    • Gibt ein Objekt zurück.
      interface Person {
          name: string;
          age: number;
      }
      
      function getPerson(): Person {
          return { name: "John", age: 30 };
      }
  5. Promise:
    • Gibt ein Promise zurück, oft bei asynchronen Operationen.
      function fetchData(): Promise<string> {
          return new Promise((resolve, reject) => {
              setTimeout(() => {
                  if (Math.random() > 0.5) {
                      resolve("Data received");
                  } else {
                      reject("Failed to fetch data");
                  }
              }, 1000);
          });
      }
      
      async function main() {
          try {
              const data = await fetchData();
              console.log(data);
          } catch (error) {
              console.error("Error:", error);
          }
      }
      
      main();

      Funktionen mit einem Return-Type Promise sind für asynchrone Operationen und geben ein Promise-Objekt zurück. Funktionen mit dieser Return-Type sind nützlich bei Operationen wie API-Aufrufe, Dateizugriffe oder Timer-basierte Aktionen.

  6. Tuple:
    • Gibt ein Tupel zurück, eine fixe Anzahl von Werten verschiedener Typen.
      function getCoordinates(): [number, number] {
          return [40.7128, -74.0060];
      }
  7. Union Typen:
    • Gibt einen Wert zurück, der mehrere Typen haben kann.
      function getValue(): string | number {
          return Math.random() > 0.5 ? "Hello" : 42;
      }

      Union-Typen werden oft verwendet, um die Flexibilität von Rückgabewerten zu erhöhen oder wenn verschiedene Arten von Ergebnissen erwartet werden.

  8. Any:
    • Kann jeden beliebigen Typ zurückgeben, sollte sparsam verwendet werden.
      function getValue(): any {
        return "Hello";
      }
      
      function main() {
        const result = getValue();
        console.log("Any Type."+result.toUpperCase());  
      }
      
      main();

      Funktionen mit dem Rückgabetyp any können einen Wert beliebigen Typs zurückgeben. Any deaktiviert die Typprüfung, was die Rückgabetypen flexibel macht, aber auch das Risiko von Laufzeitfehlern erhöht.

  9. Unknown:
    • Jeder Typ kann zurückgegeben werden. Im Vergleich zu any jedoch wird eine Typprüfung erzwungen.
      function getValue(): unknown {
        return "Hello";
      }
      
      function main() {
        const result = getValue();
        console.log(result);
        console.log("Unbekannter Typ."+result.toUpperCase());  
        //Fehler: Property 'toUpperCase' does not exist on type 'unknown'
      }
      
      main();

      In dem Beispiel wird bei console.log(result); der Inhalt der Variable ausgegeben. Bei console.log("Unbekannter Typ."+result.toUpperCase()); tritt jedoch ein Fehler auf, weil der Rückgabetyp unknown nicht eindeutig ist. In solchen Fällen sollten Type Guards verwendet werden.

      function getValue(): unknown {
        return "Hello";
      }
      
      function main() {
        const result = getValue();
        console.log(result);
          
      	// Type Guards für Typprüfungen
        if (typeof result === "string") {
          console.log(result.toUpperCase());
        } else if (typeof result === "number") {
          console.log(result.toFixed(2));
        } else if (typeof result === "boolean") {
          console.log(String(result));
        } else {
          console.log("Unbekannter Typ.");
        }
      }
      
      main();
  10. Never:
    • Gibt an, dass die Funktion nie einen Wert zurückgibt (z. B. bei Endlosschleifen oder Fehlern).
    function processValue(value: string | number | boolean): void {
      if (typeof value === "string") {
          console.log("It's a string: " + value);
      } else if (typeof value === "number") {
          console.log("It's a number: " + value);
      } else if (typeof value === "boolean") {
          console.log("It's a boolean: " + value);
      } else {
          handleUnexpectedValue(value);
      }
    }
    
    function handleUnexpectedValue(value: never): never {
      throw new Error("Unexpected value type: " + typeof value);
    }
    
    function main() {
      try {
          processValue("hello"); // Es ist ein string: hello
          processValue(42); // Es ist eine Nummer: 42
          processValue(true); // Es ist ein boolean: true      
          processValue({} as any); 
          // Dieser Aufruf führt zur Auslösung einer Ausnahme
      } catch (error) {
          console.error("Fehler abgefangen: ", error.message);
      }
    
      console.log("Dieser Code wird nach dem try-catch-Block ausgeführt.");
    }
    
    main();

    In diesem Beispiel haben wir eine Funktion erstellt, die nur 3 Typen zulässt. In der Funktion main wird die Funktion processValue vom Typ any aufgerufen, um in den else Block zu kommen. Die Funktion handleUnexpectedValue prüft erst, ob Value vom Typ never ist und bricht sie mit throw ab.

Diese Typen können kombiniert werden, um komplexere Rückgabetypen zu erstellen, je nach den Anforderungen deiner Anwendung.

Diese Seite verwendet Cookies, um die Nutzerfreundlichkeit zu verbessern. Mit der weiteren Verwendung stimmst du dem zu.

Datenschutzerklärung