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:
- 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; }
- string: Gibt eine Zeichenkette zurück.
- 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.
- Gibt keinen Wert zurück.
- Array:
- Gibt ein Array zurück, z. B. ein Array von Zahlen.
function getNumbers(): number[] { return [1, 2, 3]; }
- Gibt ein Array zurück, z. B. ein Array von Zahlen.
- Object:
- Gibt ein Objekt zurück.
interface Person { name: string; age: number; } function getPerson(): Person { return { name: "John", age: 30 }; }
- Gibt ein Objekt zurück.
- 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.
- Gibt ein Promise zurück, oft bei asynchronen Operationen.
- Tuple:
- Gibt ein Tupel zurück, eine fixe Anzahl von Werten verschiedener Typen.
function getCoordinates(): [number, number] { return [40.7128, -74.0060]; }
- Gibt ein Tupel zurück, eine fixe Anzahl von Werten verschiedener Typen.
- 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.
- Gibt einen Wert zurück, der mehrere Typen haben kann.
- 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.
- Kann jeden beliebigen Typ zurückgeben, sollte sparsam verwendet werden.
- 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. Beiconsole.log("Unbekannter Typ."+result.toUpperCase());
tritt jedoch ein Fehler auf, weil der Rückgabetypunknown
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();
- Jeder Typ kann zurückgegeben werden. Im Vergleich zu
- 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 FunktionprocessValue
vom Typany
aufgerufen, um in den else Block zu kommen. Die FunktionhandleUnexpectedValue
prüft erst, ob Value vom Typ never ist und bricht sie mitthrow
ab.
Diese Typen können kombiniert werden, um komplexere Rückgabetypen zu erstellen, je nach den Anforderungen deiner Anwendung.