Skip to content

型別系統入門

前言

為什麼 "1" + 1 在 JavaScript 裡得到 "11",在 Python 裡卻直接報錯? 這背後就是型別系統在起作用。型別系統是程式語言的「交通規則」——它決定了資料能怎麼用、能和誰運算、什麼時候檢查合不合法。理解型別系統,你就能理解不同語言的「性格差異」。

這篇文章會帶你學什麼?

學完這章後,你將獲得:

  • 分類能力:掌握靜態/動態、強/弱型別的四象限分類法
  • 問題診斷:看到 TypeError 時能快速定位是型別不匹配還是隱式轉換
  • 語言選擇:理解為什麼 TypeScript 適合大型專案、Python 適合快速原型
  • 型別推斷:理解現代語言如何兼顧簡潔和安全
  • 實踐意識:掌握型別安全的編碼習慣
章节內容核心概念
第 1 章什麼是型別系統型別的本質、為什麼需要型別
第 2 章靜態型別 vs 動態型別檢查時機、IDE 支援、安全性
第 3 章強型別 vs 弱型別隱式轉換、型別安全
第 4 章型別推斷自動推斷、兩全其美
第 5 章泛型:寫一次,適用所有型別型別參數、型別約束、複用
第 6 章型別安全實戰常見陷阱、防禦策略
第 7 章語言型別象限圖四象限分類、語言選擇

0. 全景圖:型別是資料的「身分證**

型別系統就是程式語言用來管理這些「身分」的規則體系。它回答兩個核心問題:

型別系統的兩個核心問題

  • 何時檢查? 是寫程式碼時就檢查(靜態型別),還是執行時才檢查(動態型別)?
  • 多嚴格? 是嚴格禁止混用(強型別),還是自動幫你轉換(弱型別)?

1. 什麼是型別系統:資料的交通規則

Type System ExplorerStatic vs dynamic · strong vs weak typing · type inference
StrongWeakStaticDynamic
Strong + static
JavaRustHaskell
Weak + static
CC++
Strong + dynamic
PythonRuby
Weak + dynamic
JavaScriptPHP
Strong + static
Strict compile-time checking with no implicit conversion. Very safe and IDE-friendly, but more verbose.
Compile-time checksNo implicit conversionAutocomplete-friendlySafe refactoring
Core idea:Type systems choose along two dimensions: when checks happen (static/dynamic) and whether implicit conversion is allowed (strong/weak). There is no best combination, only the best fit for a scenario.
型別系統的作用說明例子
防止非法運算阻止無意義的操作不能對字串做除法
提供文件資訊型別就是最好的文件function add(a: number, b: number) 一目了然
輔助 IDE 工具自動補全、重構、跳轉輸入 user. 自動提示所有屬性
最佳化效能編譯器知道型別後能產生更快的程式碼知道是整數就用整數指令

2. 靜態型別 vs 動態型別:什麼時候檢查?

🔍 Static vs Dynamic Typing: Live Comparison

Choose a code sample and compare how the two type systems behave

Static typing (TypeScript)⏱ Checked at compile time
let name: string = "Alice"
name = 42  // ❌ compile error
❌ Type "number" is not assignable to type "string"
VS
Dynamic typing (JavaScript)⏱ Checked at runtime
let name = "Alice"
name = 42  // ✅ OK
✅ Runs normally; name becomes 42
💡 Static typing catches the error while you write code. Dynamic typing waits until runtime.

核心區別

  • 靜態型別:變數的型別在編譯時就確定了。代表:Java、TypeScript、Rust、Go。
  • 動態型別:變數的型別在執行時才確定。代表:Python、JavaScript、Ruby、PHP。
維度靜態型別動態型別
檢查時機編譯時執行時
發現 bug
靈活性較低較高
IDE 支援較弱

趨勢:動態語言在「靜態化」

Python 加了 Type Hints,JavaScript 社群轉向 TypeScript——動態語言也在擁抱靜態型別的好處。


3. 強型別 vs 弱型別:允不允許「偷偷轉換」?

⚡ Strong vs Weak Typing: Implicit Conversion Lab

Choose an expression and see how different languages handle it

JavaScriptWeak
"1" + 1
→ "11" (string concatenation)
PythonStrong
"1" + 1
→ TypeError: can only concatenate str to str
JavaWeak
"1" + 1
→ "11" (string concatenation)
RustStrong
"1" + 1
→ compile error: type mismatch
📌 Strongly typed languages refuse to guess your intent. Weakly typed languages may helpfully convert, but the result may be wrong.

核心區別

  • 強型別:不允許隱式型別轉換,型別不匹配就報錯。
  • 弱型別:允許隱式型別轉換,語言會「好心」幫你自動轉。
維度強型別弱型別
"1" + 1報錯或需明確轉換自動轉換
安全性
可預測性

4. 型別推斷:兩全其美的現代方案

🧠 Type Inference: How the Compiler Guesses Types

Click a code line to see how the compiler infers the type step by step

1let x = 42 → number
2let names = ["Alice", "Bob"]
3let result = x > 10 ? "big" : "small"
4const add = (a: number, b: number) => a + b
5let mixed = [1, "two", true]
Inference process
1The right side is literal 42
242 is an integer-like number
3Infer x as number
Type Inference Capability by Language
Rust
Almost fully inferred
TypeScript
Most types inferred
Kotlin
Strong local inference
Go
Mainly := short declarations
Java
var keyword (Java 10+)
C
Almost none

型別推斷的價值

寫著像動態語言一樣簡潔,編譯器檢查像靜態語言一樣嚴格。

  • TypeScriptlet x = 42 自動推斷為 number
  • Rustlet v = vec![1, 2, 3] 自動推斷為 Vec<i32>
  • Gox := 42 短變數宣告自動推斷型別

5. 泛型:寫一次,適用所有型別

🧩 Generics: Write Once, Use with Any Type

Choose a scenario and see how generics keep code flexible and safe

❌ Without generics
// Need one function per type
function getFirstNumber(arr: number[]): number {
  return arr[0]
}
function getFirstString(arr: string[]): string {
  return arr[0]
}
// boolean, object... it never ends
You repeat the same code for every type.
✅ With generics
// One generic function handles all types
function getFirst<T>(arr: T[]): T {
  return arr[0]
}

getFirst<number>([1, 2, 3])   // → number
getFirst<string>(["a", "b"])  // → string
T is a type parameter and is replaced by the actual type at call time.
Type flow
T = numberarr: number[]return: number
泛型特性說明範例
泛型函式函式的參數/返回值使用型別參數function first<T>(arr: T[]): T
泛型類別類別的屬性/方法使用型別參數class Box<T> { value: T }
泛型約束用 extends 限制 T 的範圍<T extends HasLength>

6. 型別安全實戰:常見陷阱與防禦

🛡️ Type Safety in Practice: Traps and Defenses

Choose a common trap and learn how the type system protects code

⚠️ Dangerous code
function getLength(str) {
  return str.length  // what if str is null?
}
getLength(null)  // 💥 runtime crash
💥 TypeError: Cannot read properties of null
✅ Safe code
function getLength(str: string | null): number {
  if (str === null) return 0
  return str.length  // ✅ compiler knows str is not null here
}
✅ The compiler forces you to handle null
🔑 Defense strategy
  • Enable strictNullChecks
  • Use string | null to mark nullable values explicitly
  • Use optional chaining ?. for safe access

型別安全的四條黃金法則

  1. 開啟嚴格模式:TypeScript 的 strict: true
  2. 避免 any:用 unknown 代替 any
  3. 明確處理 null:用可選鏈 ?. 和空值合併 ??
  4. 為 API 定義介面:外部資料永遠不可信
陷阱危險程度防禦手段
null/undefined 參照極高strictNullChecks + 可選鏈
any 型別濫用用 unknown + 型別守衛
隱式型別轉換嚴格比較 === + ESLint

7. 語言型別象限圖

Programming Language Type ModelsHow type systems differ across languages
When types are checked
Static typing
Java, C++, Rust, Go
Dynamic typing
Python, JavaScript, Ruby
Type strength
Strong typing
Python, Java, Rust
Weak typing
JavaScript, C, PHP
Type System Classification Matrix
Static + strong
Java, C++, Rust, Go
Compile-time checks with type safety
Static + weak
C
Compile-time checks with flexible conversion
Dynamic + strong
Python, Ruby
Runtime checks with type safety
Dynamic + weak
JavaScript, PHP
Runtime checks with flexible typing
Type Inference
Modern languages can infer variable types automatically without explicit declarations.
TypeScript
let x = 5; // inferred as number
let name = "Alice"; // string
Rust
let x = 5; // inferred as i32
let name = "Alice"; // &str
象限特點代表語言適用場景
靜態 + 強型別最安全Rust, Java, Haskell大型系統
靜態 + 弱型別編譯時檢查但允許隱式轉換C, C++系統程式設計
動態 + 強型別執行時檢查,不允許隱式轉換Python, Ruby腳本、快速原型
動態 + 弱型別最靈活,也最容易出 bugJavaScript, PHPWeb 前端、小腳本

沒有「最好」的型別系統

  • 快速原型:動態型別(Python)開發速度快
  • 大型專案:靜態型別(TypeScript、Java)維護成本低
  • 系統程式設計:強型別 + 靜態(Rust)安全性最高

總結

  1. 型別是身分證:每種資料都有型別,型別決定了資料能參與什麼運算
  2. 靜態 vs 動態:何時檢查型別——編譯時還是執行時
  3. 強 vs 弱:是否允許隱式型別轉換
  4. 型別推斷:現代語言讓你享受動態的簡潔和靜態的安全
  5. 泛型:用型別參數實作程式碼複用
  6. 四象限分類:沒有最好的型別系統,只有最適合場景的選擇

延伸閱讀