TypeScript前端開發程式碼品質strict 模式升級指南2026

2026 年你該學 TypeScript 嚴格模式的 5 個理由

strict 模式是 TypeScript 最值得一開始就打開的設定。本文用實際錯誤範例說明它為什麼重要,以及如何逐步遷移到嚴格模式。

· 5 分鐘閱讀

TypeScript 6.0 正式將 strict 模式設為預設值。這意味著新專案從第一天就會在嚴格模式下開發——但對於仍在用 "strict": false 的老專案,這篇文章用 5 個實際錯誤範例,說明為什麼你應該現在就打開它。


理由一:你現在寫的 null,未來會是 undefined

最常見的問題:strictNullChecks 沒開的時候,nullundefined 可以「自由」地出現在任何類型中。

// 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.emailstring | null,呼叫 .toLowerCase()emailnull 時會在 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-ignoresas 的地方都是紅燈

在遷移過程中,你可能會看到一些 // @ts-ignoreas 的使用——這些都是技術債的訊號

// 這些不是解答,是暫時的止痛藥
// @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(寬鬆等於,會同時匹配 undefinednull)和 != 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禁止 nullundefined 出現在非預期的位置
noImplicitAny禁止隱含的 any 類型
strictFunctionTypes函式參數類型必須嚴格匹配
strictBindCallApply.bind()/.call()/.apply() 需要明確類型
strictPropertyInitialization類別屬性必須在建構式中初始化
noImplicitThisthis 被推論為 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 官方文件和多年社群遷移經驗整理。