📋 目錄
TypeScript 6.0 正式將
strict模式設為預設值。這意味著新專案從第一天就會在嚴格模式下開發——但對於仍在用"strict": false的老專案,這篇文章用 5 個實際錯誤範例,說明為什麼你應該現在就打開它。
理由一:你現在寫的 null,未來會是 undefined
最常見的問題:strictNullChecks 沒開的時候,null 和 undefined 可以「自由」地出現在任何類型中。
// strictNullChecks = false(關閉)
function getUserName(user: { name: string | null }) {
return user.name.toUpperCase(); // ❌ 編譯器不報錯,但 production 會炸
}
// strictNullChecks = true(開啟)
function getUserName(user: { name: string | null }) {
return user.name.toUpperCase();
// ❌ Error: Object is possibly 'null'. TS 明確告訴你這裡有問題
}
你以為自己處理了 null,實際上只是僥倖沒遇到。strictNullChecks 把這個僥倖變成了編譯錯誤。
理由二:noImplicitAny 是 TypeScript 最重要的紀律訓練
當 TypeScript 推論不出類型時,它預設會給你 any——一個沒有任何類型檢查的「萬能型」。any 會傳播:一旦有地方用了 any,之後所有用到這個值的地方都會失去類型保障。
// noImplicitAny = false(關閉)
// TypeScript 默默給了你 any
function processData(data) { // data 是 any
return data.value.name; // 編譯不報錯,執行時才知道問題
}
// noImplicitAny = true(開啟)
function processData(data) {
// ❌ Error: Parameter 'data' implicitly has an 'any' type
return data.value.name;
}
打開 noImplicitAny 後,TypeScript 會要求你明確宣告每一個參數的類型。這不只是一個設定,而是一種讓你自己思考「這個資料是什麼形狀」的紀律訓練。
理由三:strictFunctionTypes 讓函式參數類型更安全
TypeScript 的函式參數類型檢查,在非嚴格模式下有一個微妙的漏洞:
// 非嚴格模式:允許更寬鬆的參數類型
function greet(fn: (name: string) => void) {
fn("hello");
}
function greetWithDate(fn: (name: Date) => void) {
fn(new Date());
}
// 在 strictFunctionTypes = true 時,這種不安全的賦值會被攔截
這個坑在高階函式(higher-order functions)和回調函式的情境下特別容易踩到。strictFunctionTypes 確保你傳給函式的回調,參數類型必須完全匹配。
理由四:strictBindCallApply 讓 .bind()、.call()、.apply() 安全
JavaScript 的 .bind()、.call()、.apply() 可以改變函式的 this 上下文和參數。TypeScript 的非嚴格模式對這些呼叫方式是「寬容」的:
function greet(name: string, age: number) {
return `Hi ${name}, you are ${age}!`;
}
// 非嚴格模式:.call() 不會檢查參數
const result = greet.call(null, "Alice");
// ❌ TypeScript 不報錯,但預期傳三個參數
// 嚴格模式:明確要求類型
const result = greet.call<typeof greet, [string, number]>(null, "Alice", 30);
// 如果參數數量或類型不對,編譯器會警告
理由五:遷移到 strict 模式會讓你的錯誤從 production 移到開發時
這是最大的價值。讓我們看一個完整的例子:
// 老程式碼(非嚴格模式)
interface User {
id: number;
name: string;
email: string | null;
role: 'admin' | 'user' | 'guest';
}
function getAdminEmail(users: User[]) {
return users
.filter(u => u.role === 'admin')
.map(u => u.email.toLowerCase()); // 看似正常,但 email 可能是 null
}
在非嚴格模式下,這段程式碼編譯通過——但 u.email 是 string | null,呼叫 .toLowerCase() 在 email 為 null 時會在 production 炸掉。
在嚴格模式下:
function getAdminEmail(users: User[]) {
return users
.filter(u => u.role === 'admin')
.map(u => u.email.toLowerCase());
// ❌ Error: 'u.email' is possibly 'null'.
// 錯誤在 development 時就出現了,而不是 production
}
這就是 strict 模式的真正價值:錯誤發生的時機,從 production runtime 移到了 development 編譯時。
如何逐步遷移到 Strict 模式
第一步:開啟編譯器建議
不要一次把整個專案設成 strict——先逐個開啟各項檢查,修正錯誤,再開下一個:
// tsconfig.json:階段性開啟
{
"compilerOptions": {
// 先開這個,workload 最集中
"strictNullChecks": true,
"noImplicitAny": true,
// 確認都修完之後,再開這個
"strictFunctionTypes": true,
// 最後這個,通常影響範圍最廣
"strict": true
}
}
第二步:使用 ts-ignores 或 as 的地方都是紅燈
在遷移過程中,你可能會看到一些 // @ts-ignore 或 as 的使用——這些都是技術債的訊號:
// 這些不是解答,是暫時的止痛藥
// @ts-ignore ❌ 不推薦
// (value as string).trim() ⚠️ 需要確認 value 確實是 string
// 正確的做法是搞清楚 value 的類型
function safeTrim(value: string | null | undefined) {
if (value == null) return '';
return value.trim(); // 明確處理後,TypeScript 通過
}
第三步:使用「嚴格等於」取代「寬鬆等於」
strictNullChecks 開啟後,== null(寬鬆等於,會同時匹配 undefined 和 null)和 != null 的使用需要更明確:
// 如果你的語義是「值不存在」,用 == null
const name = user.name == null ? 'Anonymous' : user.name;
// 如果你只想要處理 undefined
const name = user.name ?? 'Anonymous';
第四步:檢查第三方函式庫的型別定義
嚴格模式下,有時會發現第三方函式庫的型別定義不夠嚴格。解決方案:
# 安裝更完整的類型定義
npm install --save-dev @types/lodash
# 如果某個模組完全沒有類型
declare module 'legacy-module' {
export function init(config: unknown): void;
}
哪些設定組成 Strict 模式?
TypeScript 的 strict: true 同時開啟了以下所有檢查:
| 設定 | 作用 |
|---|---|
strictNullChecks | 禁止 null 和 undefined 出現在非預期的位置 |
noImplicitAny | 禁止隱含的 any 類型 |
strictFunctionTypes | 函式參數類型必須嚴格匹配 |
strictBindCallApply | .bind()/.call()/.apply() 需要明確類型 |
strictPropertyInitialization | 類別屬性必須在建構式中初始化 |
noImplicitThis | this 被推論為 any 時必須明確標註類型 |
alwaysStrict | 對每個檔案使用 "use strict" |
總結:Strict 模式是技術債的及早止損
如果你的專案現在是 "strict": false,那麼升級 TypeScript 6.0 的過程中,遲早要面對這個問題。選擇主動面對(現在就開),還是是被動面對(在 TypeScript 6.0 預設 strict 後被迫處理)?
現在開的好處:
- 錯誤在開發時出現,維修成本最低
- 新進工程師看到乾淨的錯誤訊息,而不是累積多年的技術債
- 大幅提升重構時的安全性——類型錯誤會在編譯時被發現
- 跟 TypeScript 6.0 的預設值對齊
口號: npm install typescript@6,打開 strict,世界從此不同。
{
"compilerOptions": {
"strict": true
}
}
延伸閱讀
- TypeScript 6.0 RC 升級指南 — TypeScript 6.0 預設 strict 模式的完整說明
- 2026 前端框架比較 — 主流框架對 TypeScript Strict 的支援
本文基於 TypeScript 官方文件和多年社群遷移經驗整理。