开场白:为什么又写这个话题?
「interface
和 type
到底用哪个?」------几乎每个 TS 新手在 StackOverflow 上都会刷到这条灵魂拷问。
官方文档只有一句 "they are largely equivalent" ,却藏了 10 多个细节坑:
有人因为用了 type
导致无法给第三方库"打补丁";有人因为滥用 interface
把编译器拖成"风扇狂魔";还有人把联合类型写进 interface
直接爆红。
1. 一句话先记住
interface 是"可扩展的结构性契约";type 是"类型层面的万能表达式"。
------先写 interface,做不到再请 type 出山,基本不会犯错。
2. 速查表(收藏级)
场景 | 推荐 | 例子 |
---|---|---|
纯对象形状 | interface |
interface User { name: string } |
联合/元组 | type |
`type Status = 'ok' |
原始类型别名 | type |
type ID = string |
映射/条件类型 | type |
type Partial<T> = { [K in keyof T]?: T[K] } |
需要声明合并 | interface |
给 window 补属性 |
类 implements | interface |
class Cat implements Animal |
性能敏感巨型字典 | interface |
10w+ 键的 AST 节点 |
工具类型二次加工 | type |
type CreateSlice<S> = Pick<Store<S>, 'getState'> |
3. 10 维度硬核对比
3.1 语法能力
interface
只能描述对象、函数、类构造签名 ;
type
可以描述任意类型组合:联合、交叉、元组、映射、条件、模板字面量......
ts
// 联合
type Pet = 'cat' | 'dog';
// 元组
type Coord = [number, number];
// 条件
type IsArray<T> = T extends any[] ? true : false;
→ 需要"组合/变形"时,直接上 type
。
3.2 声明合并(Declaration Merging)
ts
interface User { name: string; }
interface User { age: number; } // 自动合并
type User = { name: string; };
type User = { age: number; }; // ❌ 重复标识符
给第三方库补类型、扩展 window
、global
必须 interface
。
3.3 同名属性冲突
ts
interface A { x: number; }
interface B extends A { x: string; } // ❌ 直接报错
type A = { x: number };
type B = A & { x: string }; // 不报错,但 x 成 never
interface
提前暴露错误;type
把问题推迟到使用点。
3.4 循环引用
ts
interface Tree { left: Tree; right: Tree; } // ✅ 直接递归
type Tree = { left: Tree; right: Tree; }; // ❌ 循环引用报错
想写链表、树、图,优先 interface
。
3.5 编译性能
interface
采用名义+结构混合缓存,超大项目检查更快。type
的深层交叉/联合可能触发指数展开 ,10w+ 节点场景差距明显。
微软 TS Wiki 原话:
"Use interfaces until you need to use features from type aliases."
3.6 映射类型 & 工具类型
Partial
、Record
、Exclude
、ReturnType
... 全部用 type
实现,无法用 interface 表达。
3.7 可读性
ts
type F = ((x: 'a') => 1) & ((x: 'b') => 2) extends infer R ? R : never;
这种"一行炸出 5 个关键字"的代码,用 interface
根本写不出来,也更容易把同事劝退。
→ 公共 API 优先 interface,内部黑魔法再包 type。
3.8 与 class 共舞
ts
interface Clock { tick(): void; }
class A implements Clock { tick() {} } // 语义贴合
implements
两者都支持,但 interface
更契合"契约"思想。
3.9 模块导出
无差异,都能 export
。
3.10 官方未来路线
RFC 曾讨论"让 interface 支持联合",被否决;官方仍倾向 interface 做"结构契约"。
4. 实战决策树(复制即可)
text
需要联合/元组/映射/条件/原始别名?
├─ 是 ──> 用 type
└─ 否 ──> 纯对象?
├─ 是 ──> 需要声明合并或 implements?
│ ├─ 是 ──> interface
│ └─ 否 ──> 可 interface 也可 type,**默认 interface**
└─ 否 ──> type
5. 典型案例:写组件 Props
ts
// 1. 先写契约
interface BaseProps {
title: string;
onClose: () => void;
}
// 2. 需要部分可选,再包一层 type
type DrawerProps = Partial<BaseProps> & {
width?: number;
};
// 3. 导出给外部
export { DrawerProps };
既享受合并能力,又拿到工具类型的便利。
6. 小结:一句顺口溜
"对象先 interface,变形上 type;合并靠 interface,联合找 type。"
把这张速查表贴到仓库 Wiki,下次 Code Review 就不用再拉群辩论了。
如果这篇文章帮到了你,点个 ⭐ 再走呗~ 评论区聊聊:你们团队是"interface 党"还是"type 党**"?**