5.2 טיפוסים בסיסיים פתרון
פתרון - טיפוסים בסיסיים¶
פתרון תרגיל 1¶
let city: string = "Tel Aviv";
let population: number = 460613;
let isCapital: boolean = false;
let coordinates: number[] = [32.0853, 34.7818];
let info: { name: string; country: string; founded: number } = {
name: "Tel Aviv",
country: "Israel",
founded: 1909
};
let mayor: null = null;
let nickname: undefined = undefined;
באילו מקרים ה-annotation הכרחי:
- city, population, isCapital, coordinates, info - TS מסיקה נכון לבד, אז ה-annotation לא הכרחי
- mayor ו-nickname - TS תסיק null ו-undefined בהתאמה, מה שאומר שהמשתנה יכול להכיל רק את הערך הזה. אם רוצים שהם יוכלו להחזיק ערכים אחרים בעתיד, צריך annotation מפורש כמו let mayor: string | null = null
פתרון תרגיל 2¶
א. טאפל coordinate:
ב. טאפל httpResponse:
ג. פונקציה parseEntry:
function parseEntry(entry: string): [string, number] {
let parts = entry.split(":");
let name = parts[0];
let age = parseInt(parts[1]);
return [name, age];
}
let [name, age] = parseEntry("Alice:30");
console.log(name); // "Alice"
console.log(age); // 30
ד. push לטאפל:
let pair: [string, number] = ["Alice", 30];
pair.push("extra"); // no compile error! this is a known quirk in TypeScript
console.log(pair); // ["Alice", 30, "extra"]
זו נקודת חולשה ידועה בטייפסקריפט - push לא נבדק בטאפלים. הסיבה היא שהטיפוס של push מוגדר לקבל string | number (union של טיפוסי האיברים), וזה מתקמפל. אם רוצים טאפל באמת קבוע, אפשר להשתמש ב-readonly:
let pair: readonly [string, number] = ["Alice", 30];
pair.push("extra"); // ERROR: Property 'push' does not exist on readonly tuple
פתרון תרגיל 3¶
א. enum LogLevel:
ב. string enum HttpMethod:
ג. גרסת as const:
// LogLevel as const
const LogLevel = {
Debug: 0,
Info: 1,
Warning: 2,
Error: 3,
Critical: 4
} as const;
type LogLevel = typeof LogLevel[keyof typeof LogLevel];
// type: 0 | 1 | 2 | 3 | 4
// HttpMethod as const
const HttpMethod = {
Get: "GET",
Post: "POST",
Put: "PUT",
Delete: "DELETE"
} as const;
type HttpMethod = typeof HttpMethod[keyof typeof HttpMethod];
// type: "GET" | "POST" | "PUT" | "DELETE"
ד. getMethodColor:
function getMethodColor(method: HttpMethod): string {
switch (method) {
case HttpMethod.Get: return "green";
case HttpMethod.Post: return "blue";
case HttpMethod.Put: return "orange";
case HttpMethod.Delete: return "red";
}
}
פתרון תרגיל 4¶
function processValue(value: unknown): string {
if (typeof value === "number") {
return value.toFixed(2);
}
if (typeof value === "string") {
return value.toUpperCase();
}
return String(value);
}
function getLength(value: unknown): number {
if (typeof value === "string") {
return value.length;
}
if (Array.isArray(value)) {
return value.length;
}
return 0;
}
function callIfFunction(value: unknown): void {
if (typeof value === "function") {
value();
}
}
ההבדל: עם any, הקוד מתקמפל אבל קורס ב-runtime אם הטיפוס לא מתאים (למשל processValue(true).toUpperCase() יקרוס). עם unknown, TS מכריחה אותנו לבדוק את הטיפוס לפני שימוש, ולטפל בכל המקרים.
פתרון תרגיל 5¶
א. findUser:
function findUser(users: string[], name: string): string | null {
let found = users.find(u => u === name);
return found ?? null;
}
ב. formatUser:
function formatUser(user: { name: string; email?: string }): string {
if (user.email) {
return `${user.name} (${user.email})`;
}
return `${user.name} (no email)`;
}
ג. safeParseInt:
function safeParseInt(value: string | null | undefined): number | null {
if (value === null || value === undefined) {
return null;
}
let result = parseInt(value);
if (isNaN(result)) {
return null;
}
return result;
}
פתרון תרגיל 6¶
א. טיפוסים מוסקים:
const name = "Alice"; // "Alice" (literal type - const)
let name2 = "Alice"; // string (let - can be reassigned)
const age = 30; // 30 (literal type)
let age2 = 30; // number
const isAdmin = true; // true (literal type)
const coords = [1, 2] as const; // readonly [1, 2]
let coords2 = [1, 2]; // number[]
const config = { debug: true, version: "1.0" };
// { debug: boolean; version: string } - properties are NOT literal even with const
const config2 = { debug: true, version: "1.0" } as const;
// { readonly debug: true; readonly version: "1.0" } - literal + readonly
ב. למה const arr = [1, 2, 3] הוא number[] ולא [1, 2, 3]:
כי const מונע השמה מחדש של המשתנה, אבל לא מונע שינוי התוכן של המערך. אפשר לעשות arr.push(4) או arr[0] = 99. לכן TS לא יכולה להניח שהתוכן קבוע, והיא מסיקה number[]. כדי לקבל טיפוס ליטרלי למערך, צריך as const.
ג. Season:
type Season = "spring" | "summer" | "autumn" | "winter";
function getTemperature(season: Season): number {
switch (season) {
case "spring": return 20;
case "summer": return 35;
case "autumn": return 22;
case "winter": return 12;
}
}
פתרון תרגיל 7¶
א. student:
let student: {
name: string;
age: number;
grades: number[];
address: { city: string; street: string };
graduated?: boolean;
} = {
name: "Alice",
age: 22,
grades: [95, 88, 72, 100],
address: { city: "Tel Aviv", street: "Rothschild 1" }
};
ב. readonlyPoint:
let readonlyPoint: { readonly x: number; readonly y: number } = { x: 10, y: 20 };
readonlyPoint.x = 5; // ERROR: Cannot assign to 'x' because it is a read-only property
ג. הבעיה עם inline annotations: אם יש שני משתנים מאותו טיפוס, צריך לכתוב את אותה אנוטציה פעמיים. אם משנים שדה אחד - צריך לעדכן בשני המקומות. הפתרון הוא להשתמש ב-interface או type alias - מה שנלמד בשיעור הבא.
// the problem:
let student1: { name: string; age: number; grades: number[] } = { ... };
let student2: { name: string; age: number; grades: number[] } = { ... };
// duplicated type! if we add a field, we need to update both places
פתרון תרגיל 8¶
enum Priority {
Low = "low",
Medium = "medium",
High = "high"
}
type Task = {
title: string;
priority: string;
tags: string[];
due_date: string | null;
completed: boolean;
};
function createTask(
title: string,
priority: Priority,
tags: string[],
dueDate: string | null = null
): Task {
return {
title: title,
priority: priority,
tags: tags,
due_date: dueDate,
completed: false
};
}
let tasks: Task[] = [];
let task1 = createTask("Learn TypeScript", Priority.High, ["coding", "study"]);
let task2 = createTask("Buy groceries", Priority.Low, ["personal"], "2024-12-31");
tasks.push(task1);
tasks.push(task2);
for (let task of tasks) {
let status = task.completed ? "done" : "pending";
console.log(`${task.title} - ${status}`);
}
הבדלים מפייתון:
- Optional[str] הפך ל-string | null
- ערך ברירת מחדל None הפך ל-null
- dict הפך לטיפוס מפורש Task עם כל השדות
- list[dict] הפך ל-Task[]
- priority.value בפייתון מחזיר את הערך של ה-enum, בטייפסקריפט הערך של string enum כבר הוא המחרוזת
- append הפך ל-push
- for task in tasks הפך ל-for (let task of tasks)
- f-string הפך ל-template literal