第 3 課:TypeScript 基礎類型
學習目標
- 掌握 TypeScript 的所有基礎類型及其使用場景
- 理解類型註解與類型推斷的概念和最佳實踐
- 學會使用陣列、元組和枚舉等複合類型
- 了解 any、unknown、void、never 等特殊類型
- 能夠在實際開發中正確選擇和使用適當的類型
1. 類型系統概述
1.1 為什麼需要類型?
TypeScript 的類型系統是其最核心的特性,它為 JavaScript 帶來了靜態類型檢查的能力。
類型系統的優勢:
- 早期錯誤發現 - 在編譯時就能發現類型錯誤
- 更好的開發體驗 - IDE 提供精確的自動完成和重構
- 程式碼文檔化 - 類型本身就是最好的文檔
- 重構安全性 - 大規模重構時的類型保障
- 團隊協作 - 明確的介面契約
1.2 類型註解 vs 類型推斷
類型註解 (Type Annotation)
typescript
1let name: string = "Alice";
2let age: number = 25;類型推斷 (Type Inference)
typescript
1let name = "Alice"; // 推斷為 string
2let age = 25; // 推斷為 numberTypeScript 會盡可能自動推斷類型,但在某些情況下明確的類型註解是必要的。
2. 基本原始類型
2.1 布林類型 (boolean)
布林類型只有兩個值:true 和 false。
typescript
1let isDone: boolean = false;
2let isActive: boolean = true;
3
4// 常用於條件判斷
5if (isDone) {
6 console.log("任務完成!");
7}
8
9// 函數返回布林值
10function isEven(num: number): boolean {
11 return num % 2 === 0;
12}使用場景:
- 開關狀態
- 條件判斷
- 權限檢查
- 表單驗證結果
2.2 數字類型 (number)
TypeScript 中所有數字都是浮點數,類型為 number。
typescript
1// 不同進制的數字
2let decimal: number = 42;
3let hex: number = 0x2a; // 十六進制
4let binary: number = 0b101010; // 二進制
5let octal: number = 0o52; // 八進制
6
7// 浮點數
8let price: number = 99.99;
9let pi: number = 3.14159;
10
11// 特殊數值
12let notANumber: number = NaN;
13let infinity: number = Infinity;
14let negativeInfinity: number = -Infinity;數學運算:
typescript
1let a: number = 10;
2let b: number = 3;
3
4console.log(a + b); // 13
5console.log(a - b); // 7
6console.log(a * b); // 30
7console.log(a / b); // 3.3333...
8console.log(a % b); // 1 (餘數)
9console.log(a ** b); // 1000 (次方)2.3 字串類型 (string)
字串類型用於表示文本數據。
typescript
1let color: string = "blue";
2let fullName: string = 'John Doe';
3
4// 模板字串 (Template Literals)
5let age: number = 30;
6let sentence: string = `Hello, my name is ${fullName}.
7I'll be ${age + 1} years old next month.`;
8
9// 字串方法
10let message: string = "Hello World";
11console.log(message.length); // 11
12console.log(message.toUpperCase()); // "HELLO WORLD"
13console.log(message.toLowerCase()); // "hello world"
14console.log(message.substring(0, 5)); // "Hello"模板字串的優勢:
- 支援多行字串
- 變數插值
${variable} - 表達式計算
${expression} - 更好的可讀性
3. 陣列類型 (Array)
陣列用於存儲同一類型的多個元素。
3.1 陣列定義方式
typescript
1// 方式一:類型[]
2let numbers: number[] = [1, 2, 3, 4, 5];
3let names: string[] = ["Alice", "Bob", "Charlie"];
4let flags: boolean[] = [true, false, true];
5
6// 方式二:Array<類型>
7let scores: Array<number> = [95, 87, 92];
8let colors: Array<string> = ["red", "green", "blue"];3.2 陣列操作
typescript
1let fruits: string[] = ["apple", "banana"];
2
3// 添加元素
4fruits.push("orange"); // ["apple", "banana", "orange"]
5fruits.unshift("grape"); // ["grape", "apple", "banana", "orange"]
6
7// 移除元素
8let lastFruit = fruits.pop(); // "orange"
9let firstFruit = fruits.shift(); // "grape"
10
11// 查找元素
12let index = fruits.indexOf("banana"); // 1
13let exists = fruits.includes("apple"); // true
14
15// 陣列方法
16let upperFruits = fruits.map(fruit => fruit.toUpperCase());
17let longFruits = fruits.filter(fruit => fruit.length > 5);3.3 多維陣列
typescript
1// 二維陣列
2let matrix: number[][] = [
3 [1, 2, 3],
4 [4, 5, 6],
5 [7, 8, 9]
6];
7
8// 存取元素
9console.log(matrix[0][1]); // 2
10console.log(matrix[2][0]); // 74. 元組類型 (Tuple)
元組允許表示一個已知元素數量和類型的陣列,各元素的類型不必相同。
4.1 基本元組
typescript
1// 定義元組類型
2let person: [string, number] = ["Alice", 25];
3
4// 存取元素
5console.log(person[0]); // "Alice" (string)
6console.log(person[1]); // 25 (number)
7
8// 解構賦值
9let [name, age] = person;
10console.log(name); // "Alice"
11console.log(age); // 254.2 複雜元組
typescript
1// 產品資訊:[ID, 名稱, 價格, 是否有庫存]
2let product: [number, string, number, boolean] = [1, "iPhone", 999, true];
3
4// 座標點:[x, y, z]
5let point3D: [number, number, number] = [10, 20, 30];
6
7// 函數返回多個值
8function getNameAndAge(): [string, number] {
9 return ["Bob", 30];
10}
11
12let [userName, userAge] = getNameAndAge();4.3 可選元組元素
typescript
1// 可選元素(TypeScript 3.0+)
2let optionalTuple: [string, number?] = ["hello"];
3optionalTuple = ["hello", 42]; // 也可以
4
5// 剩餘元素
6let restTuple: [string, ...number[]] = ["first", 1, 2, 3, 4];5. 枚舉類型 (Enum)
枚舉允許我們定義一組命名常數,使程式碼更具可讀性。
5.1 數字枚舉
typescript
1enum Direction {
2 Up, // 0
3 Down, // 1
4 Left, // 2
5 Right // 3
6}
7
8let dir: Direction = Direction.Up;
9console.log(dir); // 0
10
11// 自定義起始值
12enum Status {
13 Pending = 1,
14 Processing, // 2
15 Complete, // 3
16 Failed // 4
17}
18
19// 反向映射
20console.log(Status[1]); // "Pending"
21console.log(Status.Processing); // 25.2 字串枚舉
typescript
1enum LogLevel {
2 Error = "ERROR",
3 Warn = "WARN",
4 Info = "INFO",
5 Debug = "DEBUG"
6}
7
8function log(message: string, level: LogLevel) {
9 console.log(`[${level}] ${message}`);
10}
11
12log("系統啟動", LogLevel.Info); // [INFO] 系統啟動
13log("發生錯誤", LogLevel.Error); // [ERROR] 發生錯誤5.3 常數枚舉
typescript
1const enum Colors {
2 Red,
3 Green,
4 Blue
5}
6
7let color = Colors.Red; // 編譯後直接替換為 0常數枚舉的優勢:
- 編譯時內聯,減少運行時開銷
- 更好的性能
- 適用於不需要反向映射的場景
6. 特殊類型
6.1 any 類型
any 類型可以表示任何類型,關閉了 TypeScript 的類型檢查。
typescript
1let value: any = 42;
2value = "hello"; // OK
3value = true; // OK
4value = {}; // OK
5value = []; // OK
6
7// 可以調用任何方法(危險!)
8value.foo.bar.baz; // 編譯通過,運行時可能錯誤何時使用 any:
- 遷移 JavaScript 程式碼
- 處理動態內容
- 第三方庫沒有類型定義
避免過度使用:
typescript
1// 不好的做法
2let data: any = fetchUserData();
3
4// 更好的做法
5interface User {
6 id: number;
7 name: string;
8 email: string;
9}
10let data: User = fetchUserData();6.2 unknown 類型
unknown 是 any 的安全替代方案。
typescript
1let value: unknown;
2
3value = 42;
4value = "hello";
5value = true;
6
7// 不能直接使用,必須先檢查類型
8// value.toUpperCase(); // 錯誤!
9
10// 類型守衛
11if (typeof value === "string") {
12 console.log(value.toUpperCase()); // OK
13}
14
15if (typeof value === "number") {
16 console.log(value.toFixed(2)); // OK
17}unknown vs any:
unknown更安全,需要類型檢查any關閉類型檢查,更危險- 優先使用
unknown
6.3 void 類型
void 表示沒有返回值的函數。
typescript
1function logMessage(message: string): void {
2 console.log(message);
3 // 沒有 return 語句,或者 return;
4}
5
6function processData(data: any[]): void {
7 data.forEach(item => console.log(item));
8 return; // 可以有空的 return
9}
10
11// void 變數只能賦值 undefined
12let unusable: void = undefined;6.4 never 類型
never 表示永遠不會出現的值的類型。
typescript
1// 總是拋出錯誤的函數
2function throwError(message: string): never {
3 throw new Error(message);
4}
5
6// 無限循環的函數
7function infiniteLoop(): never {
8 while (true) {
9 // 永遠不會結束
10 }
11}
12
13// 窮盡性檢查
14type Shape = "circle" | "square" | "triangle";
15
16function getArea(shape: Shape): number {
17 switch (shape) {
18 case "circle":
19 return Math.PI * 5 * 5;
20 case "square":
21 return 10 * 10;
22 case "triangle":
23 return 0.5 * 8 * 6;
24 default:
25 // 如果所有情況都處理了,這裡的 shape 是 never
26 const exhaustiveCheck: never = shape;
27 throw new Error(`未處理的形狀: ${exhaustiveCheck}`);
28 }
29}6.5 null 和 undefined
typescript
1// 在 strictNullChecks: true 下
2let nullValue: null = null;
3let undefinedValue: undefined = undefined;
4
5// 聯合類型
6let nullable: string | null = "hello";
7nullable = null; // OK
8
9let optional: number | undefined = 42;
10optional = undefined; // OK
11
12// 可選屬性
13interface User {
14 name: string;
15 age?: number; // 等同於 age: number | undefined
16}7. 類型斷言
有時你比 TypeScript 更了解某個值的類型,可以使用類型斷言。
7.1 基本語法
typescript
1// 角括號語法
2let someValue: any = "this is a string";
3let strLength: number = (<string>someValue).length;
4
5// as 語法(推薦,JSX 兼容)
6let strLength2: number = (someValue as string).length;7.2 實際應用
typescript
1// DOM 操作
2let inputElement = document.getElementById("myInput") as HTMLInputElement;
3inputElement.value = "Hello";
4
5// API 響應
6interface ApiResponse {
7 data: any;
8 status: number;
9}
10
11let response: ApiResponse = await fetch("/api/data").then(r => r.json());
12let userData = response.data as User;7.3 非空斷言
typescript
1// 非空斷言操作符 !
2let maybeString: string | null = getString();
3let definitelyString: string = maybeString!; // 斷言不為 null
4
5// 謹慎使用,確保值確實不為 null/undefined8. 類型推斷深入
8.1 最佳通用類型
typescript
1// TypeScript 會推斷最佳通用類型
2let numbers = [1, 2, 3]; // number[]
3let mixed = [1, "hello", true]; // (string | number | boolean)[]
4
5// 空陣列需要明確類型
6let empty: string[] = [];8.2 上下文類型
typescript
1// 根據上下文推斷類型
2window.onmousedown = function(mouseEvent) {
3 // mouseEvent 被推斷為 MouseEvent
4 console.log(mouseEvent.button);
5};
6
7// 陣列方法的回調函數
8let numbers = [1, 2, 3];
9numbers.map(n => n.toString()); // n 被推斷為 number互動練習
練習 1:基本類型使用
熟悉基本類型的聲明和使用。
typescript
1type: simple_run
2instruction: 聲明不同類型的變數,觀察 TypeScript 的類型檢查。
3---
4// 聲明基本類型變數
5let userName: string = "Alice";
6let userAge: number = 25;
7let isActive: boolean = true;
8
9console.log(`用戶: ${userName}, 年齡: ${userAge}, 活躍: ${isActive}`);
10
11// 嘗試類型推斷
12let inferredString = "Hello TypeScript"; // 推斷為 string
13let inferredNumber = 42; // 推斷為 number
14let inferredBoolean = false; // 推斷為 boolean
15
16console.log("推斷的類型:");
17console.log(typeof inferredString); // string
18console.log(typeof inferredNumber); // number
19console.log(typeof inferredBoolean); // boolean
20
21// 展示類型安全
22// userName = 123; // 這會產生編譯錯誤
23console.log("✅ 基本類型練習完成!");typescript
1// 這個練習展示了 TypeScript 基本類型的使用
2// 重點概念:
3// 1. 明確的類型註解 vs 類型推斷
4// 2. TypeScript 的類型安全檢查
5// 3. typeof 操作符在運行時檢查類型
6
7// 最佳實踐:
8// - 簡單變數可以依賴類型推斷
9// - 函數參數和返回值建議明確註解
10// - 複雜對象建議使用介面定義練習 2:陣列操作實戰
學習陣列類型的定義和操作。
typescript
1type: output_check
2instruction: 完成購物車功能,實現商品的添加、移除和計算總價。
3expectedOutput: 購物車商品: iPhone,iPad,MacBook, 總價: $2997
4---
5// 定義商品介面
6interface Product {
7 id: number;
8 name: string;
9 price: number;
10}
11
12// 購物車類
13class ShoppingCart {
14 private items: Product[] = [];
15
16 addItem(product: Product): void {
17 this.items.push(product);
18 }
19
20 removeItem(productId: number): void {
21 this.items = this.items.filter(item => item.id !== productId);
22 }
23
24 getTotalPrice(): number {
25 return this.items.reduce((total, item) => total + item.price, 0);
26 }
27
28 getItemNames(): string[] {
29 return this.items.map(item => item.name);
30 }
31}
32
33// 測試購物車
34const cart = new ShoppingCart();
35
36// 添加商品
37cart.addItem({ id: 1, name: "iPhone", price: 999 });
38cart.addItem({ id: 2, name: "iPad", price: 599 });
39cart.addItem({ id: 3, name: "MacBook", price: 1399 });
40
41// 輸出結果
42const itemNames = cart.getItemNames().join(",");
43const totalPrice = cart.getTotalPrice();
44console.log(`購物車商品: ${itemNames}, 總價: $${totalPrice}`);typescript
1// 這個練習展示了:
2// 1. 介面定義 - Product 介面定義了商品的結構
3// 2. 陣列類型 - Product[] 表示商品陣列
4// 3. 陣列方法 - push, filter, reduce, map 等
5// 4. 類的使用 - 封裝購物車邏輯
6
7// 關鍵陣列方法:
8// - push(): 添加元素到陣列末尾
9// - filter(): 過濾符合條件的元素
10// - reduce(): 累積計算(如總和)
11// - map(): 轉換陣列元素
12
13// 類型安全的好處:
14// - 編譯時檢查商品結構
15// - 防止錯誤的方法調用
16// - 更好的 IDE 支援練習 3:元組與解構
掌握元組類型的使用和解構賦值。
typescript
1type: output_check
2instruction: 使用元組表示座標點,實現距離計算功能。
3expectedOutput: 點A: (0,0), 點B: (3,4), 距離: 5
4---
5// 定義座標點類型(元組)
6type Point = [number, number];
7
8// 計算兩點間距離的函數
9function calculateDistance(point1: Point, point2: Point): number {
10 const [x1, y1] = point1; // 解構第一個點
11 const [x2, y2] = point2; // 解構第二個點
12
13 const deltaX = x2 - x1;
14 const deltaY = y2 - y1;
15
16 return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
17}
18
19// 創建座標點
20const pointA: Point = [0, 0];
21const pointB: Point = [3, 4];
22
23// 計算距離
24const distance = calculateDistance(pointA, pointB);
25
26// 輸出結果
27console.log(`點A: (${pointA[0]},${pointA[1]}), 點B: (${pointB[0]},${pointB[1]}), 距離: ${distance}`);typescript
1// 元組的優勢:
2// 1. 固定長度和類型順序
3// 2. 比陣列更嚴格的類型檢查
4// 3. 適合表示結構化數據
5
6// 解構賦值的好處:
7// 1. 簡潔的語法
8// 2. 直接提取需要的值
9// 3. 提高程式碼可讀性
10
11// 實際應用場景:
12// - 座標點 [x, y]
13// - 顏色值 [r, g, b]
14// - 函數返回多個值
15// - 鍵值對 [key, value]
16
17// 類型別名 (Type Alias):
18// type Point = [number, number] 創建了可重用的類型定義練習 4:枚舉實戰應用
使用枚舉管理應用狀態和常數。
typescript
1type: output_check
2instruction: 實現一個任務管理系統,使用枚舉表示任務狀態和優先級。
3expectedOutput: 任務: 學習TypeScript, 狀態: 進行中, 優先級: 高, 可以開始: true
4---
5// 定義任務狀態枚舉
6enum TaskStatus {
7 Pending = "待處理",
8 InProgress = "進行中",
9 Completed = "已完成",
10 Cancelled = "已取消"
11}
12
13// 定義優先級枚舉
14enum Priority {
15 Low = 1,
16 Medium = 2,
17 High = 3,
18 Critical = 4
19}
20
21// 任務介面
22interface Task {
23 id: number;
24 title: string;
25 status: TaskStatus;
26 priority: Priority;
27}
28
29// 任務管理器
30class TaskManager {
31 private tasks: Task[] = [];
32
33 addTask(title: string, priority: Priority): void {
34 const task: Task = {
35 id: this.tasks.length + 1,
36 title,
37 status: TaskStatus.Pending,
38 priority
39 };
40 this.tasks.push(task);
41 }
42
43 updateTaskStatus(taskId: number, status: TaskStatus): void {
44 const task = this.tasks.find(t => t.id === taskId);
45 if (task) {
46 task.status = status;
47 }
48 }
49
50 canStartTask(taskId: number): boolean {
51 const task = this.tasks.find(t => t.id === taskId);
52 return task ? task.status === TaskStatus.Pending : false;
53 }
54
55 getTaskInfo(taskId: number): string {
56 const task = this.tasks.find(t => t.id === taskId);
57 if (!task) return "任務不存在";
58
59 const priorityName = Priority[task.priority] || "未知";
60 const canStart = this.canStartTask(taskId);
61
62 return `任務: ${task.title}, 狀態: ${task.status}, 優先級: ${priorityName}, 可以開始: ${canStart}`;
63 }
64}
65
66// 測試任務管理器
67const manager = new TaskManager();
68manager.addTask("學習TypeScript", Priority.High);
69manager.updateTaskStatus(1, TaskStatus.InProgress);
70
71console.log(manager.getTaskInfo(1));typescript
1// 枚舉的最佳實踐:
2
3// 1. 字串枚舉 vs 數字枚舉:
4// - 字串枚舉:更好的調試體驗,值有意義
5// - 數字枚舉:支援反向映射,更緊湊
6
7// 2. 枚舉的優勢:
8// - 集中管理常數
9// - 類型安全
10// - 自動完成支援
11// - 重構友好
12
13// 3. 實際應用:
14// - 狀態管理(如任務狀態)
15// - 配置選項
16// - 錯誤代碼
17// - API 響應狀態
18
19// 4. 注意事項:
20// - 避免異構枚舉(混合字串和數字)
21// - 考慮使用 const enum 優化性能
22// - 字串枚舉沒有反向映射練習 5:類型安全的 API 處理
使用 unknown 和類型守衛處理 API 響應。
typescript
1type: output_check
2instruction: 實現類型安全的 API 響應處理,使用 unknown 類型和類型守衛。
3expectedOutput: 用戶資料: {"id":1,"name":"Alice","email":"alice@example.com","age":25}
4---
5// 定義用戶介面
6interface User {
7 id: number;
8 name: string;
9 email: string;
10 age: number;
11}
12
13// 類型守衛函數
14function isUser(obj: unknown): obj is User {
15 return (
16 typeof obj === "object" &&
17 obj !== null &&
18 typeof (obj as User).id === "number" &&
19 typeof (obj as User).name === "string" &&
20 typeof (obj as User).email === "string" &&
21 typeof (obj as User).age === "number"
22 );
23}
24
25// 模擬 API 響應處理
26function processApiResponse(response: unknown): User | null {
27 // 使用類型守衛檢查
28 if (isUser(response)) {
29 return response; // 現在 TypeScript 知道這是 User 類型
30 }
31
32 console.log("無效的用戶資料格式");
33 return null;
34}
35
36// 模擬不同的 API 響應
37const validResponse: unknown = {
38 id: 1,
39 name: "Alice",
40 email: "alice@example.com",
41 age: 25
42};
43
44const invalidResponse: unknown = {
45 id: "1", // 錯誤:應該是 number
46 name: "Bob"
47 // 缺少必要欄位
48};
49
50// 處理響應
51const user1 = processApiResponse(validResponse);
52const user2 = processApiResponse(invalidResponse);
53
54if (user1) {
55 console.log(`用戶資料: ${JSON.stringify(user1)}`);
56}
57
58if (user2) {
59 console.log(`用戶資料: ${JSON.stringify(user2)}`);
60} else {
61 console.log("第二個響應無效");
62}typescript
1// unknown 類型的優勢:
2// 1. 比 any 更安全 - 強制類型檢查
3// 2. 適合處理外部數據(API、用戶輸入)
4// 3. 防止意外的屬性訪問
5
6// 類型守衛 (Type Guards):
7// 1. 用戶定義的類型守衛:obj is Type
8// 2. typeof 守衛:typeof x === "string"
9// 3. instanceof 守衛:x instanceof Date
10// 4. in 操作符:'property' in object
11
12// 最佳實踐:
13// 1. 對外部數據使用 unknown 而不是 any
14// 2. 創建可重用的類型守衛函數
15// 3. 在類型守衛中檢查所有必要屬性
16// 4. 處理邊界情況(null、undefined)
17
18// 實際應用:
19// - API 響應驗證
20// - 用戶輸入驗證
21// - 第三方庫集成
22// - 動態內容處理練習 6:never 類型與窮盡性檢查
使用 never 類型確保程式碼的完整性。
typescript
1type: output_check
2instruction: 實現一個形狀面積計算器,使用 never 類型進行窮盡性檢查。
3expectedOutput: 圓形面積: 78.54, 正方形面積: 100, 三角形面積: 24
4---
5// 定義形狀類型
6type Shape =
7 | { kind: "circle"; radius: number }
8 | { kind: "square"; sideLength: number }
9 | { kind: "triangle"; base: number; height: number };
10
11// 面積計算函數
12function calculateArea(shape: Shape): number {
13 switch (shape.kind) {
14 case "circle":
15 return Math.PI * shape.radius * shape.radius;
16
17 case "square":
18 return shape.sideLength * shape.sideLength;
19
20 case "triangle":
21 return 0.5 * shape.base * shape.height;
22
23 default:
24 // 窮盡性檢查:如果所有情況都處理了,這裡的 shape 是 never
25 const exhaustiveCheck: never = shape;
26 throw new Error(`未處理的形狀類型: ${exhaustiveCheck}`);
27 }
28}
29
30// 錯誤處理函數
31function handleError(message: string): never {
32 throw new Error(message);
33}
34
35// 測試不同形狀
36const circle: Shape = { kind: "circle", radius: 5 };
37const square: Shape = { kind: "square", sideLength: 10 };
38const triangle: Shape = { kind: "triangle", base: 8, height: 6 };
39
40try {
41 const circleArea = calculateArea(circle);
42 const squareArea = calculateArea(square);
43 const triangleArea = calculateArea(triangle);
44
45 console.log(`圓形面積: ${circleArea.toFixed(2)}, 正方形面積: ${squareArea}, 三角形面積: ${triangleArea}`);
46} catch (error) {
47 console.log(`計算錯誤: ${error.message}`);
48}typescript
1// never 類型的應用場景:
2
3// 1. 窮盡性檢查:
4// - 確保 switch/if-else 處理所有情況
5// - 當添加新的聯合類型成員時,編譯器會提醒更新處理邏輯
6
7// 2. 永不返回的函數:
8// - 總是拋出錯誤的函數
9// - 無限循環的函數
10
11// 3. 不可達的程式碼:
12// - 理論上永遠不會執行的程式碼分支
13
14// 聯合類型 (Union Types):
15// - 使用 | 分隔多個類型
16// - 每個成員有判別屬性(如 kind)
17// - TypeScript 可以根據判別屬性縮小類型
18
19// 最佳實踐:
20// 1. 在 switch 語句的 default 中使用 never 檢查
21// 2. 為聯合類型添加判別屬性
22// 3. 使用 never 確保函數不會意外返回
23// 4. 利用 never 進行編譯時完整性檢查練習 7:類型推斷與斷言
掌握 TypeScript 的類型推斷機制和類型斷言的使用。
typescript
1type: output_check
2instruction: 實現一個通用的數據處理器,展示類型推斷和類型斷言的使用。
3expectedOutput: 處理字串: HELLO WORLD, 處理數字: 42.00, 處理陣列: [1,2,3,4,5]
4---
5// 通用數據處理器
6class DataProcessor {
7 // 類型推斷:根據參數自動推斷返回類型
8 processString(input: string) {
9 return input.toUpperCase(); // 推斷返回 string
10 }
11
12 processNumber(input: number) {
13 return input.toFixed(2); // 推斷返回 string
14 }
15
16 processArray<T>(input: T[]) {
17 return [...input, input[input.length - 1]]; // 推斷返回 T[]
18 }
19
20 // 處理未知類型的數據
21 processUnknown(input: unknown): string {
22 // 使用類型守衛
23 if (typeof input === "string") {
24 return `處理字串: ${this.processString(input)}`;
25 }
26
27 if (typeof input === "number") {
28 return `處理數字: ${this.processNumber(input)}`;
29 }
30
31 if (Array.isArray(input)) {
32 // 類型斷言:我們知道這是數字陣列
33 const numberArray = input as number[];
34 const processed = this.processArray(numberArray);
35 return `處理陣列: [${processed.join(",")}]`;
36 }
37
38 return "未知類型";
39 }
40
41 // DOM 操作中的類型斷言
42 setupInputElement(elementId: string): void {
43 // 類型斷言:我們知道這是 input 元素
44 const element = document.getElementById(elementId) as HTMLInputElement;
45 if (element) {
46 element.value = "預設值";
47 element.addEventListener("change", (e) => {
48 const target = e.target as HTMLInputElement;
49 console.log(`輸入值: ${target.value}`);
50 });
51 }
52 }
53}
54
55// 測試數據處理器
56const processor = new DataProcessor();
57
58// 測試不同類型的數據
59const stringResult = processor.processUnknown("hello world");
60const numberResult = processor.processUnknown(42);
61const arrayResult = processor.processUnknown([1, 2, 3, 4]);
62
63console.log(`${stringResult}, ${numberResult}, ${arrayResult}`);
64
65// 展示類型推斷
66let inferredString = processor.processString("test"); // 推斷為 string
67let inferredNumber = processor.processNumber(3.14159); // 推斷為 string
68let inferredArray = processor.processArray([1, 2, 3]); // 推斷為 number[]typescript
1// 類型推斷的工作原理:
2
3// 1. 變數初始化:
4// let x = 5; // 推斷為 number
5// let y = "hello"; // 推斷為 string
6
7// 2. 函數返回值:
8// function add(a: number, b: number) {
9// return a + b; // 推斷返回 number
10// }
11
12// 3. 上下文類型:
13// window.onclick = (e) => {
14// // e 被推斷為 MouseEvent
15// };
16
17// 類型斷言的使用場景:
18
19// 1. DOM 操作:
20// const input = document.getElementById("id") as HTMLInputElement;
21
22// 2. API 響應:
23// const data = response.data as UserData;
24
25// 3. 聯合類型縮小:
26// const value = getValue() as string;
27
28// 注意事項:
29// 1. 類型斷言不是類型轉換
30// 2. 只能斷言為更具體或更寬泛的類型
31// 3. 謹慎使用,確保斷言的正確性
32// 4. 優先使用類型守衛而不是類型斷言總結
在這一課中,我們全面學習了 TypeScript 的基礎類型系統:
關鍵要點
- 基本類型:boolean、number、string 是最常用的原始類型
- 複合類型:陣列、元組、枚舉提供了更豐富的數據結構
- 特殊類型:any、unknown、void、never 各有特定的使用場景
- 類型安全:unknown 比 any 更安全,應該優先使用
- 類型推斷:TypeScript 能自動推斷大部分類型,但關鍵位置仍需明確註解
最佳實踐
- 啟用
strictNullChecks提高類型安全性 - 優先使用
unknown而不是any - 使用類型守衛進行安全的類型檢查
- 利用
never類型進行窮盡性檢查 - 在函數參數和公共 API 中明確標註類型
下一步
- 學習更複雜的類型操作
- 掌握介面和類的使用
- 了解泛型的強大功能
- 探索高級類型特性
準備好了嗎?讓我們在下一課學習變數宣告和作用域!