[Note] TypeScript Course for Beginners - Learn TypeScript from Scratch!
這個 TypeScript 學習大綱包含從基礎到進階的概念,涵蓋了 TypeScript 的基本語法、編譯器配置、高級類型和特性、泛型、裝飾器、命名空間和模組,以及 TypeScript 與其他工具的集成。此大綱適用於想要全面學習 TypeScript 的開發者,從基本開始,然後深入理解 TypeScript 的高級功能。
Get Started
這一部分涵蓋了 TypeScript 的入門和基本概念。可能會涉及以下主題:
- Using Types:介紹基本的 TypeScript 型別概念
- Type Assignment and Type Inference:解釋型別指定和型別推斷
💡 The key difference: JavaScript uses “dynamic types” (resolved at runtime), TypeScript uses “static types” (set during development)
TypeScript Basics
這一部分主要介紹 TypeScript 的基本語法和結構,包括基本的 JavaScript 型別和 TypeScript 特有的型別:
-
Numbers, Strings and Booleans:基礎的型別
1let name: string = 'John'; 2let age: number = 25; 3let isStudent: boolean = true; 4 5function greet(person: string): string { 6 return `Hello, ${person}!`; 7} 8 9console.log(greet(name)); 10
-
Object Types:對象型別
-
Array Types:陣列型別
-
Tuples:元組
- 元組的長度是固定的,不可隨意增加或刪除元素
- 元組中元素的型別和順序必須符合定義
- 在使用元組時,TypeScript 編譯器會檢查型別和順序,以確保符合定義
1// 定義一個元組,包含兩個數字 2let point: [number, number] = [1, 2]; 3 4// 定義一個包含名字和年齡的元組 5let person: [string, number] = ['Alice', 25]; 6
-
Enums:枚舉。枚舉的優點包括:
- 可讀性:使用描述性的名稱代替數字或字串,可以使代碼更容易理解
- 可維護性:當需要改變常量值時,只需修改枚舉的定義,避免在代碼中到處查找和修改
- 防止錯誤:使用枚舉可以減少使用非預期值的風險
1enum Direction { 2 NORTH = 'N', 3 EAST = 'E', 4 SOUTH = 'S', 5 WEST = 'W', 6} 7 8console.log(Direction.NORTH); // 輸出:"N" 9
當定義枚舉時,常見的做法是使用駝峰命名法來定義枚舉的名稱,而使用全大寫來定義枚舉的常量值。這樣做可以在代碼中清楚地標識枚舉的值和其他變數,增強可讀性,有助於區分與普通變數的差異。然而,這不是嚴格規定的規則,而是一種慣例。
-
The Any Type:任意型別
-
Union Types:聯合型別
- Union Types 用
|
符號將多個型別聯合起來,表明參數可以是其中之一
1function printValue(value: number | string): void { 2 if (typeof value === 'number') { 3 console.log(`The number is ${value}`); 4 } else { 5 console.log(`The string is '${value}'`); 6 } 7} 8 9printValue(42); // 輸出:The number is 42 10printValue('Hello'); // 輸出:The string is 'Hello' 11
- Union Types 用
-
Literal Types:字面量型別
-
Literal Types 允許你使用特定的值作為型別,而不僅僅是使用更通用的型別(如
number
或string
) -
定義一組固定的可能值,並限制其他值的使用
例如,如果你要定義一個代表方向的變數,它只能是 "north"、"south"、"east" 或 "west",可以使用字面量型別來限定可能的值:
1type Direction = 'north' | 'south' | 'east' | 'west'; 2 3function move(direction: Direction): void { 4 console.log(`Moving in the ${direction} direction`); 5} 6 7move('north'); // 輸出:Moving in the north direction 8// move('up'); // 錯誤:'up' 不是有效的方向 9
-
-
Type Aliases:型別別名
- 別名可以指向基本型別、聯合型別、元組型別、函數型別,甚至更複雜的結構
1type Name = string; // 給 string 型別取個別名 2type StringOrNumber = string | number; // 聯合型別 3type Direction = 'north' | 'south' | 'east' | 'west'; // 字面量型別 4// 物件型別別名 5type Person = { 6 name: string; 7 age: number; 8}; 9
-
Function Return Types and Void:函數返回型別和空型別
-
在函數定義中指定函數應返回的型別。這有助於確保函數的返回值符合預期,並允許 TypeScript 編譯器進行型別檢查 例如,下面是一個返回數字的函數,它明確指定了返回型別為
number
:1function getSquare(num: number): number { 2 return num * num; 3} 4 5const result = getSquare(5); // 25 6
-
void
型別用於表示函數沒有返回值。這在定義那些不需要返回特定值的函數時非常有用,如事件處理器、日誌記錄函數等1function logMessage(message: string): void { 2 console.log(message); 3} 4 5logMessage('Hello, TypeScript'); 6
-
-
Function Types and Callbacks:函數型別與回呼
-
函數型別(Function Types)用於指定一個函數的參數和返回值的型別
1// 定義一個接受兩個數字參數並返回數字的函數型別 2type MathOperation = (a: number, b: number) => number; 3 4// 使用函數型別 5const add: MathOperation = (x, y) => x + y; 6const subtract: MathOperation = (x, y) => x - y; 7 8console.log(add(5, 3)); // 8 9console.log(subtract(5, 3)); // 2 10
-
回呼函數(Callbacks)是函數型別的一個常見應用。它們允許你將函數作為參數傳遞,並在某些事件或操作完成後調用。 例如,定義一個回呼函數型別,用於處理事件:
1// 定義一個函數型別,接受一個字串參數且沒有返回值 2type EventCallback = (event: string) => void; 3 4// 使用回呼函數 5function triggerEvent(callback: EventCallback, eventName: string): void { 6 console.log(`Triggering event: ${eventName}`); 7 callback(eventName); // 呼叫回呼函數 8} 9 10const handleEvent: EventCallback = (event) => { 11 console.log(`Event handled: ${event}`); 12}; 13 14triggerEvent(handleEvent, 'ClickEvent'); 15
在這個例子中,
EventCallback
是一個回呼函數型別,定義了它接受一個字串參數且沒有返回值。這種回呼機制在事件驅動程式和異步操作中非常常見。
-
-
The Unknown Type:未知型別
unknown
型別表示一個變數可能是任何型別- 它是比
any
更嚴格的通用型別,因為在使用unknown
型別時,需要執行型別檢查或斷言,才能將其轉換為其他型別 unknown
的使用場景包括從外部來源接收數據、處理不確定的值、或者在函數中接受多種可能的參數
1let value: unknown; 2 3value = 42; // 可以是數字 4value = 'Hello'; // 也可以是字串 5 6// 不能直接將 unknown 型別賦值給其他具體型別 7let name: string; 8// name = value; // 錯誤,因為 unknown 不能直接賦值 9 10// 必須執行型別檢查 11if (typeof value === 'string') { 12 name = value; // 這時可以賦值,因為已確定是字串 13} 14
在這個例子中,我們使用
unknown
表示一個不確定型別的變數。要將unknown
轉換為具體型別,需要執行型別檢查或型別斷言。 -
The Never Type:永不型別
-
never
型別表示一個函數或表達式永遠不會有返回值,通常是因為它會拋出異常、進行無限循環、或呼叫process.exit()
這類終止程序的操作 -
never
型別常見於以下幾種情況:- 函數拋出異常:當函數永遠不會正常返回,因為它總是拋出異常
- 無限循環:當函數進入無限循環而不會退出
- 編譯器不應該到達的地方:通常用於確保代碼的完整性
1// throwError 總是拋出異常 2function throwError(message: string): never { 3 throw new Error(message); // 總是拋出異常 4} 5 6// infiniteLoop 進行無限循環 7function infiniteLoop(): never { 8 while (true) { 9 // 這是一個無限循環 10 } 11} 12 13// assertNever 用於在 switch 語句中處理不可能的情況 14function assertNever(x: never): never { 15 throw new Error('Unexpected value: ' + x); 16} 17 18type Shape = 'circle' | 'square' | 'triangle'; 19 20function getShapeArea(shape: Shape): number { 21 switch (shape) { 22 case 'circle': 23 return Math.PI * 5 * 5; // 圓形面積 24 case 'square': 25 return 25; // 正方形面積 26 case 'triangle': 27 return 10; // 三角形面積 28 default: 29 assertNever(shape); // 處理不可能的情況 30 } 31} 32
-
Compiler & Configuration Deep Dive
tsconfig.json
是 TypeScript 專案的配置文件,用於指定編譯器選項、項目文件、型別檢查等。
基礎的 tsconfig.json
配置主要涉及編譯器選項、文件包含與排除,以及編譯目標等,以下是一些基礎選項的簡單說明。
Compiler Options
target
:指定編譯後的 JavaScript 版本,例如ES5
、ES6
、ES2020
等,這個選項決定了生成的 JavaScript 代碼的兼容性module
:指定模組系統,如CommonJS
、ES6
、AMD
等,這個選項決定了模組的使用方式。strict
:開啟嚴格模式,包含一系列嚴格的型別檢查設定,這是建議的配置,能夠增強型別安全性outDir
:指定編譯後輸出的目錄,這個選項決定編譯後的文件位置rootDir
:指定源代碼的根目錄,這可以確保正確的目錄結構sourceMap
:啟用或禁用源地圖,用於在調試時映射到原始 TypeScript 源碼declaration
:生成 TypeScript 的聲明文件(.d.ts
),用於外部型別定義removeComments
:在編譯後移除註釋noImplicitAny
:如果變數未指定型別,則不允許默認為any
。這是一個嚴格模式的選項
File Inclusion and Exclusion
include
:指定要包含在編譯中的文件或目錄,例如:["src//\*"]
表示包含src
** 目錄及其子目錄的所有文件exclude
:指定不包含在編譯中的文件或目錄,例如:["node_modules", "dist"]
1{ 2 "compilerOptions": { 3 "target": "ES6", 4 "module": "CommonJS", 5 "strict": true, 6 "outDir": "dist", 7 "rootDir": "src", 8 "sourceMap": true, 9 "declaration": true, 10 "removeComments": true, 11 "noImplicitAny": true 12 }, 13 "include": ["src//*"], 14 "exclude": ["node_modules", "dist"] 15} 16