博客 / 詳情

返回

TypeScript 中 Type 和 Interface 傻傻分不清?看完這篇就不糾結了

你是不是也有過這樣的困惑:

打開一個老項目,或者在做 Code Review 時,發現代碼裏一會兒是 interface Props,一會兒又是 type State。問同事為什麼要混用,他也支支吾吾説不出個所以然,最後只能來一句:“哎呀,反正都能跑,看心情唄。”

但在 TypeScript 的世界裏,“能跑”和“寫得好”是兩碼事。

type(類型別名)和 interface(接口)這對雙胞胎,在 TS 誕生之初就一直相愛相殺。它們確實太像了,像到在絕大多數 CRUD 業務開發中,你閉着眼隨便選一個都不會報錯。

但是,作為一個追求代碼質量的開發者,我們不能止步於此。

今天,我們跳出表面的語法糖,從底層機制入手,徹底搞清楚它們的本質區別,並給你一套最佳實踐方案。拒絕選擇困難症。

1. 表象:90% 的重合度與其誤區

為什麼大家會糾結?因為在定義“對象”的形狀(Shape)時,它們長得幾乎一模一樣。

看看下面的代碼,你能一眼看出區別嗎?

// 使用 interface
interface UserI {
  name: string;
  age: number;
}

// 使用 type
type UserT = {
  name: string;
  age: number;
}

在日常使用中,如果你想實例化一個對象,或者在函數參數中約束類型,這倆兄弟的表現是完全一致的。它們都支持:

  • 定義對象結構
  • 定義函數簽名
  • 支持泛型
  • 支持類(Class)的實現(implements)

但這正是新手容易陷入的誤區:以為它們是可以隨意互換的同義詞。 實際上,TS 設計這兩個概念,是為了解決完全不同的問題。

2. 核心:聲明合併、類型表達與擴展性

區別不僅存在,而且在關鍵時刻決定了你的架構設計是否合理。主要體現在以下三個核心維度:

2.1 聲明合併 (Declaration Merging) —— Interface 的必殺技

這是 interface 獨有的特性,也是它存在的最大理由。

場景模擬:

你引入了一個第三方庫(比如 Vue 或 jQuery),但你發現它的全局對象上少了一個你需要的屬性。這時,如果你用 interface,你可以直接在自己的代碼裏“補”上這個屬性。

// 假設這是第三方庫定義的 interface
interface User {
  name: string;
}

// 你的代碼中再次定義同名 interface
interface User {
  age: number;
}

// ✨ TS 會自動把它們縫合在一起!
const me: User = {
  name: "Gemini",
  age: 18 // 必須兩個屬性都有,否則報錯
};

反觀 type:

它是封閉的(Closed)。一旦定義,無法通過同名方式修改。

type User = {
  name: string;
};

// ❌ 報錯:Duplicate identifier 'User'.
type User = {
  age: number;
};

💡 結論: interface 具有開放性,允許後續擴展;而 type 具有封閉性,更適合確定的業務邏輯。

2.2 類型表達能力 —— Type 的主場

type 的全稱是 Type Alias(類型別名)。既然是別名,它就能給任何東西起名字,不僅僅是對象。

在處理複雜類型時,Type 的靈活性完勝 Interface:

  • 聯合類型 (Union Types): 前端開發中最常用的功能。

    type Status = 'pending' | 'success' | 'failed';
    type ID = string | number;

    Interface 無法直接定義這種“或”的關係。

  • 元組 (Tuple):

    type Point = [number, number];
  • 類型體操:

    當你使用 Pick、Omit、Record 或者條件類型(Conditional Types)時,產出的結果通常都是 type。

2.3 擴展方式:Extends vs Intersection

雖然兩者都能實現“繼承”的效果,但語義不同。

  • Interface 使用 extends:側重於面向對象的層級繼承。
  • Type 使用 & (交叉類型):側重於集合的合併。

雖然通常可以互通,但在處理衝突屬性時,interface 會直接報錯提醒,而交叉類型(&)可能會產生 never 類型,導致錯誤提示不夠直觀。


3. 規範:一套拿來即用的最佳實踐

講了這麼多理論,回到最初的問題:我們在項目中到底該怎麼選?

與其每次都糾結,不如遵循這套簡單的 “二選一法則”,這也符合目前主流大廠(如 Google 規範)和 React 社區的推薦趨勢:

場景一:你在編寫庫 (Library) 或第三方包

請優先使用 interface

理由: 作為庫的作者,你需要為你的用户留出“後路”。用户可能需要利用“聲明合併”的特性,向你的全局接口中注入自定義屬性(比如擴展 Window 對象或給 Request 對象增加 user 字段)。使用 Interface 是對使用者的尊重。

場景二:你在編寫業務應用 (Application / UI 組件)

請優先使用 type

理由:

  1. 一致性 (Consistency): 既然 type 能搞定對象、聯合類型、元組等所有情況,而 interface 只能搞定對象,那麼全員使用 type 可以讓代碼風格更統一。
  2. 安全性 (Safety): 在業務代碼中,我們通常不希望定義好的類型被莫名其妙地“自動合併”了(這是隱患)。type 的報錯提醒能讓你更安全。
  3. React 生態: 現在的 React 社區更傾向於用 type 來定義 PropsState,因為它在處理組件複合類型時更加直觀。

總結

為了方便記憶,我做了一張對比速查表:

特性 Interface Type
核心理念 描述對象的形狀 (Shape) 任何類型的別名 (Alias)
聲明合併 支持 (自動合併) ❌ 不支持 (會報錯)
**聯合類型 ( )** ❌ 不支持
映射/條件類型 ❌ 不支持 ✅ 支持
最佳使用場景 編寫庫 (Library) 編寫應用 (App)

一句話口訣:

對外 API(庫)用 Interface,對內業務邏輯用 Type。如果你實在拿不準,就用 Type,直到你必須用 Interface 為止。

你們團隊的代碼規範裏,是強制用 type 還是 interface?還是像大部分項目一樣“隨緣混用”?

歡迎在評論區留言,我們一起聊聊 TS 裏的那些坑!

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.