interface 与 type 的终极对比深度解析
interface 支持声明合并(给第三方库打补丁),type 能表达联合、交叉、元组、映射类型(interface 做不到)。 公开库类型用 interface 留扩展点,带 variant 的 Props 用 type 写联合,多数对象两者皆可。
一句话概括
interface 能声明合并(给第三方库打补丁),type 能表达联合/交叉/元组/映射类型(interface 做不到)。这不是喜好选择题——库的公开类型用 interface 留扩展点,组件 Props 带 variant 用 type 写联合类型,大多数对象定义两者都行。
核心知识点
1. 对象定义——大部分场景等价
1
2
3
4
5
interface User { name: string; age: number }
type User = { name: string; age: number }
// 都能被 class implements、能被 extends 扩展、能定义函数签名
class Admin implements User { name = ''; age = 0 }
2. 声明合并——interface 的独门绝技
1
2
3
4
5
6
7
8
9
10
11
12
// 同名 interface 自动合并
interface User { name: string }
interface User { age: number }
// 最终类型:{ name: string; age: number }
// 🔑 真正价值:给第三方库扩展类型
declare interface Window {
__INITIAL_STATE__: AppState; // 不覆盖 Window 原有属性,只是追加
}
// type 重复定义 → 直接报错
// type User = { age: number } // ❌ Duplicate identifier
注意: 非函数同名成员类型冲突会报错;同名函数成员自动按声明顺序合并为重载。
3. type 的独占领域——interface 做不到的
1
2
3
4
5
6
7
8
9
10
11
12
13
// 联合类型 —— type 的王牌
type Status = 'pending' | 'success' | 'error';
// 元组
type Point = [number, number];
// 映射类型 / 条件类型 —— 只能用 type
type Readonly<T> = { readonly [K in keyof T]: T[K] };
type ReturnOf<T> = T extends (...args: any[]) => infer R ? R : never;
// 从值提取类型
const config = { host: 'localhost', port: 3000 };
type Config = typeof config;
4. 扩展方式的隐藏差异——extends vs & 的致命区别
1
2
3
4
5
6
7
8
9
10
11
12
13
interface A { a: string }
interface B extends A { b: number } // extends
type T = A & { b: number } // 交叉 &
// 可以互相配合
interface C extends A { c: Date } // interface extends type ✅
type D = A & { d: Date } // type & interface ✅
// ⚠️ 致命差异:同名属性冲突
interface X { a: number }
// interface Y extends X { a: string } // ❌ 编译直接报错 —— 立刻发现!
type Z = X & { a: string } // ✅ 编译通过 —— 但 a 的类型是 never!
extends 遇到冲突立刻报错帮你发现;& 把冲突属性静默合并成 never——编译过了但那个属性没法用。 此外,interface 是命名类型(可缓存复用),type 的交叉类型需要递归展平——大规模类型检查时 interface 性能更优。
5. 选型决策表
| 场景 | 推荐 | 原因 |
|---|---|---|
| 库的公开 API 类型 | interface | 留声明合并扩展点 + 性能更好 |
| 组件 Props(含 variant 联合) | type | 需要 'a' \| 'b' 联合类型 |
| 映射/条件/工具类型 | type | interface 做不到 |
| 给第三方库补丁 | interface | 声明合并 |
| 日常对象定义 | 团队统一 | 两者都行 |
其实你每天都在用
- 给
window补丁:declare interface Window { appConfig: Config }—— 只有 interface 能不破坏原有类型 - 组件 variant Props:
type BtnProps = { variant: 'primary' \| 'danger' } & HTMLAttributes—— 联合 + 交叉一把梭 - 开源库 .d.ts 全是 interface:Vue、React 的类型定义大量用 interface,给你留扩展点且性能更优
- API 响应泛型:
interface ApiRes<T> { code: number; data: T }—— 对象形状用 interface 语义更贴切 - 后端枚举约束:
type Role = 'admin' \| 'editor' \| 'viewer'—— 字面量联合只能用 type
常见误解(FAQ)
❌ 误区:「interface 是 type 的子集」 恰恰相反——两者是大部分重叠 + 各自独占:type 独占联合/映射/条件类型;interface 独占声明合并。不是谁包含谁,是交集 + 互补。
❌ 误区:「type 不适合大型项目因为不能 extends」 type 用
&组合,表达能力甚至更强。但extends遇到冲突报错更早、interface 检查性能更好——这才是选extends的真实理由,而不是”type 不能 extends”。❌ 误区:「extends 和 & 遇到冲突都报错」 不一样。
extends直接报红帮你发现问题。&把冲突属性合并成never——编译通过了但运行时废了。很多人踩这个坑后坚定站extends。❌ 误区:「React 官方用 interface,我也用」 React 类型定义源码中两者都大量使用。TS 官方文档的态度是:说清楚用哪个都行,无硬性标准。团队统一 > 个人偏好。
一句话总结
interface 给公开 API 留扩展,type 给复杂类型做表达。不是谁更好,而是谁更适合你要解决的问题——成熟项目 .d.ts 里两者混用才是常态。