1.6: In TypeScript umwandeln
Gegeben sei das untenstehende node-Programm in JavaScript. Analysiere den Quellcode und wandle ihn anschliessend in TypeScript um.
Analyse
- Kopiere den JavaScript Code in eine neue Datei
example.jsim Ordner1-6-umwandeln. - Sieh dir den Quellcode genau an. Überlege, was das Programm genau macht.
- Fülle die
<???>-Lücken in den Kommentaren mit einer kurzen Beschreibung des jeweiligen Codeblocks. - Führe das Programm aus mit
node 1-6-umwandeln/example.js. - Überlege, was der Code für Probleme aufweist, die mit TypeScript gelöst werden könnten.
Umwandlung zu TypeScript
- Kopiere die Datei und benenne die Kopie um in
example.ts. - Sieh dir die Datei im Editor an. Was für Fehler werden angezeigt?
- Versuche die Datei trotzdem zu kompilieren mit
tsc --target esnext --module nodenext example.tsund vergleiche die Ursprungsdatei mit dem Resultat. - Führe das Programm aus mit
node ./example.js.
Anreicherung mit Typen
-
Erstelle die Typ-Definitionen anhand der Dokumentation
-
Finde heraus wie ein
User-Objekt aussieht und erstelle dafür einen entsprechendentype -
Finde heraus wie ein
Post-Objekt aussieht und erstelle dafür einen entsprechendentype -
Finde heraus wie ein
Comment-Objekt aussieht und erstelle dafür einen entsprechendentype -
Reichere die Funktionen mit den typisierten Signaturen an und verwende dafür die erstellten Typ-Definitionen. Asynchrone Funktionen verwenden den Rückgabe-Typ
Promise<>. -
Ergänze die Signatur von
fetchUsersmit dem Rückgabe-TypPromise<User[]> -
Ergänze die Signatur von
fetchPostsmit einem Rückgabe- und Parameter-Typ -
Ergänze die Signatur von
fetchCommentsmit einem Rückgabe- und Parameter-Typ
Abfangen von Spezialfällen
- Stelle sicher, dass alle Funktionen die korrekten, typisierten Werte zurückgeben
- Fange alle Spezialfälle ab, indem du zusätzliche Verzweigungen und Prüfungen im Code implementierst
- Stelle sicher, dass dein Code auch mit
--strictkompiliert, alsotsc --strict --target esnext --module nodenext example.ts - Vergleiche nochmals den kompilierten JavaScript Code mit der Ursprungsdatei. Was hat sich verändert?
Weitere Verbesserungen (optional)
- Nimm weitere Verbesserungen am Code vor, begründe jeweils, warum du etwas so gemacht hast
// `readline` und `cli` werden benötigt, um Eingaben vom Benutszer abzufragenimport * as readline from 'node:readline/promises';import { stdin as input, stdout as output } from 'node:process';const cli = readline.createInterface({ input, output });
// <???>async function fetchUsers() { const res = await fetch('https://jsonplaceholder.typicode.com/users'); return res.json();}
// <???>async function fetchPosts(userId) { const res = await fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`); return res.json();}
// <???>async function fetchComments(postId) { const res = await fetch(`https://jsonplaceholder.typicode.com/comments?postId=${postId}`); return res.json();}
// <???>async function askText(question) { const answer = await cli.question(question); return answer.trim();}
// <???>async function askNumber(question) { const answer = await cli.question(question); return parseInt(answer);}
// <???>async function askYesNo(question) { const answer = await cli.question(question); return answer.trim().toLowerCase() === 'y';}
async function main() { while (true) {
// <???> const users = await fetchUsers(); for (const user of users) { console.log(`@${user.username}: ${user.name}`); }
// <???> const username = (await askText("\nEnter a username: @")); const user = users.find(user => user.username.toLowerCase() === username.toLowerCase());
// <???> console.log(`\n@${user.username} Posts:`); console.log(`Name: ${user.name}`); console.log(`Email: ${user.email}`); console.log(`Address: ${user.address.street}, ${user.address.suite}, ${user.address.city}, ${user.address.zipcode}`); console.log(`Location: 📌 Lat ${user.address.geo.lat}, Lng ${user.address.geo.lng}`);
// <???> const posts = await fetchPosts(user.id); for (const post of posts) { console.log(`${post.id}: ${post.title}`); }
// <???> const postId = await askNumber("\nEnter a post ID: "); const comments = await fetchComments(postId); for (const comment of comments) { console.log(`💬 ${comment.email}: ${comment.body}\n`); }
// <???> const startOver = await askYesNo("\nDo you want to start over? (y/n): "); if(startOver == true) { console.log("\n\nStarting over...\n"); } else { console.log("\n\nShutting down...\n"); break; } }
// `cli` muss am Programmende wieder geschlossen werden cli.close();}
main();Lösungsvorschlag
// `readline` und `cli` werden benötigt, um Eingaben vom Benutzer abzufragenimport * as readline from 'node:readline/promises';import { stdin as input, stdout as output } from 'node:process';const cli = readline.createInterface({ input, output });
type User = { id: number; name: string; username: string; email: string; address: { street: string; suite: string; city: string; zipcode: string; geo: { lat: string; lng: string; }; }; phone: string; website: string; company: { name: string; catchPhrase: string; bs: string; };};
type Post = { userId: number; id: number; title: string; body: string;};
type Comment = { postId: number; id: number; name: string; email: string; body: string;};
// Ruft die Liste der Benutzer von der API abasync function fetchUsers(): Promise<User[]> { const res = await fetch('https://jsonplaceholder.typicode.com/users'); return res.json() as unknown as User[];}
// Ruft die Liste der Beiträge eines bestimmten Benutzers von der API abasync function fetchPosts(userId: number): Promise<Post[]> { const res = await fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`); return res.json() as unknown as Post[];}
// Ruft die Liste der Kommentare zu einem bestimmten Beitrag von der API abasync function fetchComments(postId: number): Promise<Comment[]> { const res = await fetch(`https://jsonplaceholder.typicode.com/comments?postId=${postId}`); return res.json() as unknown as Comment[];}
// Fragt den Benutzer nach einer Texteingabeasync function askText(question: string): Promise<string> { const answer: string = await cli.question(question); return answer.trim();}
// Fragt den Benutzer nach einer Zahleneingabeasync function askNumber(question: string): Promise<number> { const answer: string = await cli.question(question); const parsed = parseInt(answer, 10); if (isNaN(parsed)) { console.log("❌ Invalid number input. Please try again."); return askNumber(question); // Call the function again } return parsed;}
// Fragt den Benutzer nach einer Ja/Nein-Antwortasync function askYesNo(question: string): Promise<boolean> { const answer: string = await cli.question(question); const normalized = answer.trim().toLowerCase(); if (normalized !== 'y' && normalized !== 'n') { console.log("❌ Invalid input. Please enter 'y' or 'n'."); return askYesNo(question); // Call the function again } return normalized === 'y';}
async function main(): Promise<void> { while (true) { // Ruft die Benutzerliste ab und zeigt sie an const users = await fetchUsers(); for (const user of users) { console.log(`@${user.username}: ${user.name}`); }
// Fragt den Benutzer nach einem Benutzernamen und sucht den entsprechenden Benutzer const username: string = await askText("\nEnter a username: @"); const user: User | undefined = users.find(user => user.username.toLowerCase() === username.toLowerCase());
if (!user) { console.log("❌ User not found. Please try again."); continue; }
// Zeigt Informationen über den ausgewählten Benutzer an console.log(`\n@${user.username} Posts:`); console.log(`Name: ${user.name}`); console.log(`Email: ${user.email}`); console.log(`Address: ${user.address.street}, ${user.address.suite}, ${user.address.city}, ${user.address.zipcode}`); console.log(`Location: 📌 Lat ${user.address.geo.lat}, Lng ${user.address.geo.lng}`);
// Ruft die Beiträge des Benutzers ab und zeigt sie an const posts = await fetchPosts(user.id); for (const post of posts) { console.log(`${post.id}: ${post.title}`); }
// Fragt den Benutzer nach einer Beitrags-ID und zeigt die zugehörigen Kommentare an const postId: number = await askNumber("\nEnter a post ID: "); const post: Post | undefined = posts.find(post => post.id === postId);
if (!post) { console.log("❌ Post not found. Please try again."); continue; }
const comments = await fetchComments(postId); for (const comment of comments) { console.log(`💬 ${comment.email}: ${comment.body}\n`); }
// Fragt den Benutzer, ob er von vorne beginnen möchte const startOver: boolean = await askYesNo("\nDo you want to start over? (y/n): "); if (startOver) { console.log("\n\nStarting over...\n"); } else { console.log("\n\nShutting down...\n"); break; } }
// `cli` muss am Programmende wieder geschlossen werden cli.close();}
main();