TypeScript 則是一種靜態型別語言,通過在編譯階段進行型別檢查來提前捕捉在運行時可能會遇到意外的錯誤。這可以幫助我們避免許多潛在的 bug,使得程式碼更加穩定與可維護。
在 JavaScript 中,錯誤通常是在程式執行時才會出現,這些錯誤稱為「運行時錯誤」。而 TypeScript 會在程式碼還沒有執行之前,也就是在編譯階段,進行型別檢查和錯誤報告,這被稱為「編譯階段錯誤」。這樣可以讓我們在開發過程中盡早發現問題,並減少運行時錯誤的機會。以下是常見的編譯錯誤情形:
假設我們有一個簡單的變數定義:
let age: number = 18;
如果之後嘗試將 age 賦值為一個字串:
age = "eighteen"; // 錯誤:Type 'string' is not assignable to type 'number'.
TypeScript 會立即報錯,因為 age 被定義為 number 型別,而我們卻嘗試將一個 string 賦值給它。這就是一個典型的編譯階段錯誤。
另一個常見的情況是函數參數的型別檢查:
function greet(name: string) {
  console.log(`Hello, ${name}`);
}
greet("Annie");  // 正確
greet(123);      // 錯誤:Argument of type 'number' is not assignable to parameter of type 'string'.
在這裡,greet 函數只接受 string 型別的參數,因此當我傳入 123(一個 number 或非 string 型別)時,TypeScript 會在編譯階段報錯,幫助及時發現問題。
TypeScript 的編譯階段錯誤檢查功能是內建的,只要你使用 TypeScript 進行開發,就會自動啟用這個功能。但其實也可以進一步強化檢查,讓 TypeScript 更加嚴格。
tsconfig.json 是 TypeScript 的配置檔案,我們可以在其中啟用更嚴格的型別檢查。在專案中創建 tsconfig.json,並加入以下設定:
{
  "compilerOptions": {
    "strict": true
  }
}
strict 模式會開啟 TypeScript 中的所有嚴格檢查規則,讓型別錯誤更加明顯。這包含了 strictNullChecks、strictFunctionTypes、strictPropertyInitialization 等等檢查。
假設我們有一個簡單的程式:
let message: string;
console.log(message); // 錯誤:Variable 'message' is used before being assigned.
由於 strict 模式下,TypeScript 會檢查變數是否已經初始化,因此這段程式碼會在編譯階段報錯,原因是 message 還沒被賦值。
TypeScript 不僅僅是檢查型別是否正確,還會檢查程式邏輯中潛在的錯誤。例如,當我們處理 null 或 undefined 時,TypeScript 可以幫助我們避免常見的錯誤。
在 JavaScript 中,null 和 undefined 是常見的錯誤來源。如果沒有適當處理這些值,可能會導致程式崩潰。
function printName(name: string | null) {
  console.log(name.toUpperCase()); // 錯誤:Object is possibly 'null'.
}
在這裡,name 參數可能是 null,但因為目前沒有處理這種情況,因此 TypeScript 在編譯階段就會提醒我們這個潛在問題。解決方法是加入一個檢查:
function printName(name: string | null) {
  if (name !== null) {
    console.log(name.toUpperCase());
  }
}
現在 TypeScript 就不會報錯了!因為我們已經確保 name 在被使用之前不是 null。
有時候我們知道某個值在某些情況下肯定不會是 null 或 undefined,這時我們可以使用 TypeScript 的非空斷言操作符(!)來告訴編譯器「放心,這個值一定不會是 null 或 undefined」。
function printName(name: string | null) {
  console.log(name!.toUpperCase()); // 我們確信 name 不會是 null
}
這樣,TypeScript 就不會再進行 null 檢查,但需要謹慎使用這個特性,因為一旦斷言錯誤,可能會導致運行時錯誤。
TypeScript 的編譯階段錯誤檢查提供了許多好處:
以下是完整的範例,展示了 TypeScript 在編譯階段檢查不同型別錯誤的過程:
// 定義一個變數並賦值
let age: number = 18;
// 變數賦值錯誤
age = "eighteen"; // 錯誤:Type 'string' is not assignable to type 'number'.
// 函數參數型別檢查
function greet(name: string) {
  console.log(`Hello, ${name}`);
}
greet("Annie");  // 正確
greet(123);      // 錯誤:Argument of type 'number' is not assignable to parameter of type 'string'.
// 處理 Null 和 Undefined
function printName(name: string | null) {
  if (name !== null) {
    console.log(name.toUpperCase());
  } else {
    console.log("Name is null");
  }
}
printName("Annie"); // 輸出:ANNIE
printName(null);  // 輸出:Name is null