5.2 טיפוסים בסיסיים הרצאה
טיפוסים בסיסיים - basic types¶
בשיעור הקודם ראינו שטייפסקריפט מוסיפה טיפוסים לג׳אווהסקריפט. עכשיו נכיר לעומק את כל הטיפוסים הבסיסיים שהשפה מציעה.
טיפוסים פרימיטיביים - primitives¶
מחרוזת - string¶
let firstName: string = "Alice";
let greeting: string = `Hello, ${firstName}!`; // template literals work too
let empty: string = "";
מספר - number¶
בטייפסקריפט (כמו בג׳אווהסקריפט) יש טיפוס מספרי אחד בלבד - number. הוא מכסה שלמים ועשרוניים:
let age: number = 30;
let price: number = 9.99;
let negative: number = -10;
let hex: number = 0xff;
let binary: number = 0b1010;
זה שונה מפייתון שם יש int ו-float בנפרד. בטייפסקריפט הכל number.
בוליאני - boolean¶
שימו לב: boolean ולא bool כמו בפייתון.
מערכים - arrays¶
שתי דרכים שקולות להגדיר מערך:
// syntax 1: type[]
let numbers: number[] = [1, 2, 3];
let names: string[] = ["Alice", "Bob"];
// syntax 2: Array<type> (generic syntax - we'll learn more about this later)
let scores: Array<number> = [100, 95, 88];
שני התחבירים זהים לגמרי. הקונבנציה הנפוצה היא type[] - קצר וקריא.
מערך ריק צריך טיפוס מפורש, אחרת TS תסיק any[]:
let items: string[] = []; // good - TS knows this is string[]
let stuff = []; // bad - TS infers any[]
השוואה לפייתון¶
טאפל - tuple¶
טאפל הוא מערך עם אורך קבוע וטיפוס מוגדר לכל מיקום:
// a tuple with exactly 2 elements: string and number
let person: [string, number] = ["Alice", 30];
// access by index
console.log(person[0]); // "Alice" - TS knows this is string
console.log(person[1]); // 30 - TS knows this is number
// errors
person[0] = 42; // ERROR: Type 'number' is not assignable to type 'string'
person[2]; // ERROR: Tuple type has no element at index '2'
זה דומה לטאפל של פייתון, אבל בפייתון טאפלים הם immutable ובטייפסקריפט הם mutable (אפשר לשנות ערכים, רק לא את הטיפוסים).
// common use case: function returning multiple values
function getNameAndAge(): [string, number] {
return ["Alice", 30];
}
let [name, age] = getNameAndAge(); // destructuring works!
אינום - enum¶
enum מגדיר קבוצה של קבועים בעלי שם:
enum Direction {
Up,
Down,
Left,
Right
}
let dir: Direction = Direction.Up;
console.log(dir); // 0 - enums are numeric by default
כברירת מחדל, הערכים הם מספרים שמתחילים מ-0. אפשר לשנות:
enum HttpStatus {
OK = 200,
NotFound = 404,
InternalError = 500
}
console.log(HttpStatus.OK); // 200
אינום מחרוזתי - string enum¶
enum Color {
Red = "RED",
Green = "GREEN",
Blue = "BLUE"
}
let favoriteColor: Color = Color.Red;
console.log(favoriteColor); // "RED"
string enums הם נפוצים יותר בפרקטיקה כי קל יותר לדבג אותם (רואים "RED" במקום 0).
השוואה לפייתון¶
הערה: בפרויקטים מודרניים הרבה מפתחים מעדיפים as const על פני enum - נראה את זה בהמשך.
הטיפוסים המיוחדים¶
any - ביטול בדיקת טיפוסים¶
any מכבה את בדיקת הטיפוסים לחלוטין. כל דבר מותר:
let value: any = 42;
value = "hello"; // ok
value = true; // ok
value.foo.bar; // ok - no checking at all!
value(); // ok - TS doesn't care
אל תשתמשו ב-any אלא אם אין ברירה. הוא מבטל את כל היתרונות של טייפסקריפט. אם strict: true מופעל, TS תתריע כש-any מוסק בשקט (noImplicitAny).
unknown - הטיפוס הבטוח ל"לא יודע"¶
unknown הוא כמו any, אבל בטוח - חייבים לבדוק את הטיפוס לפני שמשתמשים בו:
let value: unknown = 42;
value = "hello"; // ok - can assign anything
// but can't USE it without checking first:
value.toUpperCase(); // ERROR: 'value' is of type 'unknown'
(value as string).toUpperCase(); // ok - type assertion
if (typeof value === "string") {
value.toUpperCase(); // ok - TS narrows to string
}
כלל: כשלא יודעים מה הטיפוס, השתמשו ב-unknown ולא ב-any.
never - טיפוס שלעולם לא קורה¶
never מייצג ערך שלא יכול להתקיים. שימושים עיקריים:
// function that never returns (throws or infinite loop)
function throwError(message: string): never {
throw new Error(message);
}
// exhaustive checking - we'll see this more in lesson 5.4
function handleShape(shape: "circle" | "square"): number {
switch (shape) {
case "circle": return 3.14;
case "square": return 4;
default:
const exhaustiveCheck: never = shape; // ERROR if we miss a case
return exhaustiveCheck;
}
}
void - פונקציה שלא מחזירה ערך¶
void הוא בערך כמו None בפייתון כטיפוס החזרה, אבל לא בדיוק. פונקציה עם void יכולה להחזיר undefined בלבד, או לא להחזיר כלום.
null ו-undefined¶
בטייפסקריפט עם strictNullChecks (שמופעל כחלק מ-strict: true), null ו-undefined הם טיפוסים נפרדים:
let a: string = null; // ERROR: Type 'null' is not assignable to type 'string'
let b: string = undefined; // ERROR: Type 'undefined' is not assignable to type 'string'
// if a variable CAN be null, you must say so explicitly:
let c: string | null = null; // ok
let d: string | undefined = undefined; // ok
סימן השאלה בפרמטר אופציונלי הוא קיצור ל-| undefined:
function greet(name?: string): string {
// name is string | undefined here
return `Hello, ${name ?? "stranger"}!`;
}
הסקת טיפוסים - type inference¶
טייפסקריפט מסיקה טיפוסים אוטומטית בהרבה מקרים. בואו נראה את הכללים:
// TS infers from the initial value
let x = 42; // number
let y = "hello"; // string
let z = true; // boolean
let arr = [1, 2, 3]; // number[]
// TS infers function return type from the return statement
function add(a: number, b: number) {
return a + b; // return type inferred as number
}
// TS infers from context
let names = ["Alice", "Bob", "Charlie"];
names.map(name => name.toUpperCase()); // TS knows 'name' is string
מתי TS לא יכולה להסיק¶
// function parameters - must annotate
function greet(name) { } // ERROR with strict: Parameter 'name' implicitly has 'any' type
// empty arrays
let items = []; // any[] - must annotate: let items: string[] = [];
// variable without initial value
let result; // any - must annotate: let result: number;
הכלל: אם TS מסיקה נכון, אל תכתבו את הטיפוס. אם לא - כתבו.
טיפוסים ליטרליים - literal types¶
טיפוס ליטרלי הוא טיפוס שמייצג ערך ספציפי אחד בלבד:
let direction: "up" | "down" | "left" | "right" = "up";
direction = "up"; // ok
direction = "down"; // ok
direction = "sideways"; // ERROR: Type '"sideways"' is not assignable
let httpStatus: 200 | 404 | 500 = 200;
httpStatus = 200; // ok
httpStatus = 201; // ERROR
טיפוסים ליטרליים חזקים מאוד בשילוב עם union types. נרחיב עליהם בשיעור 5.5.
הבדל בין let ל-const בהסקה¶
let x = "hello"; // type: string (can be reassigned to any string)
const y = "hello"; // type: "hello" (literal type - can never change)
let n = 42; // type: number
const m = 42; // type: 42
כש-TS רואה const, היא יודעת שהערך לעולם לא ישתנה ומסיקה טיפוס ליטרלי. עם let, היא מסיקה טיפוס רחב יותר כי הערך יכול להשתנות.
as const¶
as const הופך ערך לקבוע לחלוטין - כל הערכים הופכים לליטרלים ולקריאה בלבד:
// without as const
let colors = ["red", "green", "blue"]; // string[]
// with as const
let colorsConst = ["red", "green", "blue"] as const;
// type: readonly ["red", "green", "blue"]
colorsConst[0] = "yellow"; // ERROR: Cannot assign to '0' because it is read-only
colorsConst.push("purple"); // ERROR: Property 'push' does not exist on type 'readonly [...]'
as const שימושי במיוחד עם אובייקטים:
const config = {
apiUrl: "https://api.example.com",
timeout: 5000,
retries: 3
} as const;
// type: { readonly apiUrl: "https://api.example.com"; readonly timeout: 5000; readonly retries: 3 }
config.apiUrl = "other"; // ERROR: Cannot assign to 'apiUrl' because it is read-only
as const כתחליף ל-enum¶
הרבה מפתחים מעדיפים as const על פני enum:
// instead of enum:
const Direction = {
Up: "UP",
Down: "DOWN",
Left: "LEFT",
Right: "RIGHT"
} as const;
type Direction = typeof Direction[keyof typeof Direction];
// type: "UP" | "DOWN" | "LEFT" | "RIGHT"
let dir: Direction = "UP"; // ok
היתרון: הקוד יותר צפוי, אין הפתעות של runtime behavior שיש ב-enum, ובקומפילציה ל-JS אין קוד מיותר.
אנוטציות על אובייקטים - object annotations¶
אפשר להגדיר את צורת האובייקט ישירות (inline):
let user: { name: string; age: number; isAdmin: boolean } = {
name: "Alice",
age: 30,
isAdmin: false
};
שדות אופציונליים¶
let product: { name: string; price: number; description?: string } = {
name: "Laptop",
price: 999
// description is optional - can be omitted
};
קריאה בלבד - readonly¶
let point: { readonly x: number; readonly y: number } = { x: 10, y: 20 };
point.x = 5; // ERROR: Cannot assign to 'x' because it is a read-only property
כשהאובייקט מורכב, כתיבת הטיפוס inline הופכת למסורבלת. בשיעור הבא נלמד על interface ו-type שפותרים את הבעיה הזו.
השוואה מסכמת לפייתון¶
| פייתון | טייפסקריפט | הערה |
|---|---|---|
str |
string |
|
int, float |
number |
טיפוס מספרי אחד בלבד |
bool |
boolean |
|
list[int] |
number[] |
|
tuple[str, int] |
[string, number] |
|
Enum |
enum |
או as const |
Any |
any |
|
None |
void / undefined |
|
Optional[str] |
string \| undefined |
|
Literal["a", "b"] |
"a" \| "b" |
סיכום¶
- הטיפוסים הבסיסיים:
string,number,boolean - מערכים:
type[]אוArray<type> - טאפלים:
[type1, type2]- מערך עם אורך וטיפוסים קבועים - enum מגדיר קבוצת קבועים, אבל
as constהוא אלטרנטיבה מודרנית anyמכבה את בדיקת הטיפוסים - להימנעunknownהוא ה-anyהבטוח - חייבים לבדוק לפני שימושneverמייצג ערך שלא קורהvoidלפונקציות שלא מחזירות ערךnullו-undefinedהם טיפוסים נפרדים עם strictNullChecks- TS מסיקה טיפוסים אוטומטית - כתבו רק כשצריך
- טיפוסים ליטרליים מגבילים לערך ספציפי:
"up" | "down" as constהופך ערכים לליטרלים readonly- אפשר לתאר צורת אובייקט inline, אבל לטיפוסים מורכבים עדיף interface/type