前言
在 TypeScript 的類型系統中,有四種特殊類型經常讓人混淆:undefined、null、unknown 和 never。它們各自有不同的語意和使用場景,正確理解這些類型是寫出型別安全程式碼的基礎。
這篇文章將逐一介紹這四種類型的特點與使用方式,並透過實際範例來說明它們的差異。最後會整理一份最佳實踐與常見注意事項,幫助你在日常開發中做出正確的選擇。
undefined
undefined 代表變數已宣告但尚未賦值,是 JavaScript 中未初始化變數的默認值。
undefined 的特點
- 表示變數存在但沒有值
- 是變數的預設狀態
- 常用於可選參數
undefined 的使用場景
以下範例展示 undefined 在可選屬性、未初始化變數和可選參數中的應用:
// 可選屬性
interface User {
name: string;
age?: number; // 可能是 undefined
}
// 未初始化的變數
let userInput: string | undefined;
// 可選參數
function greet(name?: string) {
console.log(name ? `Hello ${name}` : "Hello");
}
在這個範例中:
age?表示該屬性是可選的,未提供時值為undefineduserInput明確宣告可能為undefinedname?參數在未傳入時自動為undefined
null
null 表示一個刻意被設置為「空值」的變數,與 undefined 不同,它需要明確賦值。
null 的特點
- 表示刻意的空值
- 需要明確賦值
- 用於表示當前無效或不可用的狀態
null 的使用場景
以下範例展示 null 在狀態管理和 API 響應中的應用:
// 表示不存在的狀態
class UserService {
private currentUser: User | null = null;
logout() {
this.currentUser = null; // 明確設置空值
}
}
// API 響應可能為空
interface ApiResponse {
data: Data | null;
error: Error | null;
}
在這個範例中:
currentUser使用null表示「目前沒有登入的使用者」- API 響應中的
data和error使用null表示「該欄位目前沒有值」
unknown
unknown 是 TypeScript 中最安全的「不確定」類型,比 any 更嚴格。
unknown 的特點
- 比
any更安全 - 需要類型檢查才能使用
- 不能直接訪問屬性或調用方法
unknown 的使用場景
以下範例展示如何搭配類型檢查和類型守衛來安全地處理 unknown 類型:
// API 響應處理
function processApiResponse(response: unknown) {
// 需要類型檢查
if (typeof response === "string") {
console.log(response.toUpperCase());
}
// 使用類型守衛
if (isUser(response)) {
console.log(response.name);
}
}
// 類型守衛
function isUser(value: unknown): value is User {
return typeof value === "object" && value !== null && "name" in value;
}
在這個範例中:
response的類型是unknown,不能直接使用- 透過
typeof進行類型檢查後,TypeScript 會自動收窄類型 - 自訂類型守衛
isUser()讓我們可以安全地存取User的屬性
never
never 表示永遠不會發生或永遠不會有返回值的類型。
never 的特點
- 表示不可能的狀態
- 用於永不返回的函數
- 用於完整性檢查(exhaustive check)
- 在類型運算中作為過濾器
never 的使用場景
以下範例展示 never 在錯誤拋出、完整性檢查和類型運算中的應用:
// 永不返回的函數
function throwError(message: string): never {
throw new Error(message);
}
// 完整性檢查
type Color = "red" | "green" | "blue";
function handleColor(color: Color) {
switch (color) {
case "red":
return "處理紅色";
case "green":
return "處理綠色";
case "blue":
return "處理藍色";
default:
// 確保所有可能的值都被處理
const exhaustiveCheck: never = color;
return exhaustiveCheck;
}
}
// 類型運算
type NonNullable<T> = T extends null | undefined ? never : T;
在這個範例中:
throwError()永遠不會正常返回,所以回傳類型是neverdefault分支利用never確保所有Color值都被處理過——如果未來新增了顏色卻忘了處理,TypeScript 會報錯NonNullable利用never在條件類型中過濾掉null和undefined
類型比較
| 類型 | 用途 | 示例場景 |
|---|---|---|
| undefined | 未初始化的變數 | 可選參數,未賦值的變數 |
| null | 明確的空值 | 清除狀態,表示不存在 |
| unknown | 不確定的類型 | API 響應,外部數據 |
| never | 不可能的類型 | 異常處理,完整性檢查 |
最佳實踐
undefined vs null 的選擇
- 優先使用
undefined表示「可能不存在」 - 使用
null表示「明確的空值」 - 在同一個專案中保持一致性
型別檢查方式
// 檢查 null
if (value !== null) {
}
// 檢查 undefined
if (value !== undefined) {
}
if (typeof value !== "undefined") {
}
// 檢查 unknown
if (typeof value === "string") {
}
使用 unknown 而不是 any
unknown強制你進行類型檢查,提供更好的類型安全性- 適合處理外部數據,例如 API 響應或第三方套件回傳值
使用 never 進行完整性檢查
- 在
switch語句中確保處理所有可能的值 - 在類型運算中過濾不可能的情況
注意事項
避免使用 == 進行空值檢查
// ❌ 不推薦:會同時檢查 null 和 undefined
if (value == null) {
}
// ✅ 推薦:明確檢查
if (value === null) {
}
if (value === undefined) {
}
unknown 類型的值需要類型檢查
function process(value: unknown) {
// ❌ 錯誤:不能直接使用
value.toString();
// ✅ 正確:先進行類型檢查
if (typeof value === "string") {
value.toString();
}
}
never 類型的正確使用
// 用於確保處理所有可能的情況
function assertNever(x: never): never {
throw new Error("Unexpected object: " + x);
}
總結
TypeScript 的四種特殊類型各有明確的語意和適用場景:
- 🔹 undefined:變數已宣告但尚未賦值,適用於可選參數和屬性
- 🔹 null:刻意設置的空值,適用於明確清除狀態的場景
- 🔒 unknown:比
any更安全的不確定類型,強制進行類型檢查後才能使用 - 🚫 never:不可能發生的類型,適用於完整性檢查和類型過濾
記住兩個核心原則:優先使用 unknown 取代 any 來處理外部數據,以及善用 never 搭配 switch 進行完整性檢查,能讓你的 TypeScript 程式碼更加型別安全。