在前端面试中,TypeScript 相关问题的占比逐年升高,而「type 和 interface 有什么区别」几乎是 TS 面试的第一道必考题。
很多同学只能说出 "一个用 extends 继承,一个用 & 合并" 这种表层答案,很容易被面试官追问到哑口无言。其实这两者的差异,本质上是设计理念的不同:interface 是 "契约式的接口定义",天生开放可扩展;type 是 "灵活的类型别名",主打全能与组合。
本文就从语法特性、设计逻辑到实战选型,一次性讲透 type 与 interface 的共同点与核心区别,看完不仅能应付面试,还能在项目中用得更合理。
一、先搞懂:两者的共同点是什么?
很多人一上来就讲区别,其实先理清共同点,才能更好地理解两者的定位。
interface 和 type 都是 TypeScript 中用来定义类型结构的核心语法,核心作用都是做类型约束,它们在基础场景下几乎可以互换:
1. 都可以描述对象结构
无论是普通对象还是带方法的对象,两者都能实现完全等价的约束效果:
typescript
// interface 写法
interface User {
id: number;
name: string;
sayHello(): void;
}
// type 写法
type User = {
id: number;
name: string;
sayHello(): void;
};
2. 都可以定义函数类型
两者都能描述函数的参数和返回值类型,都可用于函数参数、返回值的类型注解:
typescript
// interface 调用签名写法
interface AddFunc {
(a: number, b: number): number;
}
// type 函数类型表达式写法
type AddFunc = (a: number, b: number) => number;
3. 都支持泛型、索引签名
两者都可以搭配泛型使用,也都支持索引签名来描述动态键的对象:
typescript
// 泛型支持
interface Box<T> { value: T }
type Box<T> = { value: T };
// 索引签名
interface Dictionary { [key: string]: number }
type Dictionary = { [key: string]: number };
二、核心区别:6 个维度彻底分清
讲完共同点,我们来看最核心的差异,这也是面试的核心采分点。
1. 类型覆盖范围:type 全能,interface 仅支持对象
这是两者最基础的区别:interface 只能用来定义对象类型,而 type 可以定义任意类型。
type 支持但 interface 做不到的场景:
-
基本类型别名
ini// ✅ type 可以 type UserId = string; type StatusCode = number; // ❌ interface 不支持,语法完全错误 interface UserId = string; -
联合类型
ini// ✅ type 可以 type Status = 'success' | 'error' | 'loading'; type Result = User | null; // ❌ interface 无法原生表达联合类型 -
元组类型
typescript// ✅ type 可以 type DataTuple = [string, number, boolean]; // ❌ interface 无法直接表达元组结构
简单总结:只要不是纯对象结构的类型,基本都要用 type 来定义。
2. 扩展继承:语法不同,行为也有差异
两者都支持类型扩展,但语法和底层处理逻辑完全不同:
interface使用extends关键字继承type使用交叉类型&进行合并
基础示例
typescript
// interface 继承
interface BaseUser { id: number }
interface Admin extends BaseUser { role: string }
// type 交叉合并
type BaseUser = { id: number };
type Admin = BaseUser & { role: string };
面试深水区:同名属性的处理逻辑不同
这是 90% 的人答不上来的细节点:
-
interface extends时,如果父子接口同名属性类型不兼容,会直接报错,强制保证类型一致性:cssinterface A { value: string } // ❌ 报错:Interface 'B' incorrectly extends interface 'A' interface B extends A { value: number } -
type交叉时,同名属性不会语法报错,而是会将类型合并为never(因为string & number不存在合理类型):initype A = { value: string }; type B = A & { value: number }; // ✅ 语法不报错,但 value 的实际类型为 never
补充:两者可以互相扩展 ------interface 可以 extends 一个对象类型的 type,type 也可以通过 & 合并一个 interface。
3. 声明合并:interface 天生支持,type 完全禁止
这是 interface 独有的核心特性,也是它不可替代的原因:同名的 interface 会自动进行声明合并 ,而 type 重复定义会直接报错。
kotlin
// ✅ interface 声明合并
interface User { id: number }
interface User { name: string }
// 最终合并为 { id: number; name: string }
const user: User = { id: 1, name: '张三' }; // 正常使用
ini
// ❌ type 重复声明直接报错
type User = { id: number };
type User = { name: string };
// 错误:Duplicate identifier 'User'
声明合并的核心使用场景:扩展第三方库的类型、给全局对象添加属性。比如给 Window 对象扩展自定义属性、给 Vue 实例扩展全局属性,都必须靠 interface 的声明合并来实现。
从设计理念上说,interface 是开放的,type 是封闭的------ 接口生来就是用来定义 "可扩展的契约",而类型别名只是给一个固定类型起名字。
4. 函数类型定义:语法形式有差异
虽然两者都能定义函数类型,但表达形式和适用场景有明显区别:
-
type定义函数类型更简洁,和箭头函数写法高度一致:typescripttype FetchData = (url: string, params?: object) => Promise<any>; -
interface定义函数类型使用「调用签名」语法:typescriptinterface FetchData { (url: string, params?: object): Promise<any>; }
interface 的调用签名有一个独特优势:适合定义带属性的函数对象(函数本身还有额外属性),在封装库、定义工具函数类型时非常常用:
typescript
interface Counter {
(): number; // 调用签名:函数本身
count: number; // 函数的属性
reset(): void; // 函数的方法
}
5. 高级类型能力:type 独有的类型编程
TypeScript 的高级类型特性(映射类型、条件类型、infer 推断、模板字面量类型等),全部只能通过 type 来实现,interface 完全不支持。
我们日常使用的工具类型 Partial、Required、Pick 等,本质上都是用 type 实现的:
typescript
// 映射类型:只能用 type 实现
type MyPartial<T> = {
[P in keyof T]?: T[P];
};
// 条件类型:只能用 type 实现
type IsString<T> = T extends string ? true : false;
如果你需要做类型体操、编写工具类型、处理复杂类型运算,那 type 是唯一选择。
6. 类实现的细节差异
在类 implements 的场景中,两者都可以被类实现,但有一个细节差异:
类可以 implements 一个 interface,也可以 implements 一个对象类型的 type。但如果 type 定义的是联合类型,类无法实现:
typescript
type UserType = { id: number } | { name: string };
// ❌ 报错:类只能实现对象类型或对象交叉类型
class User implements UserType {}
而 interface 因为只能是对象类型,所以不存在这个问题,语义上也更符合 "类实现接口" 的面向对象概念。
三、实战选型:什么时候用哪个?
讲完所有区别,很多同学还是会纠结:写代码的时候到底该用哪个?
这里给你一套通用的选型原则,简单好记,也符合 TS 社区的主流实践:
表格
| 场景 | 推荐使用 | 核心原因 |
|---|---|---|
| 定义组件 Props、API 响应、公共对象契约 | interface | 可扩展、支持声明合并,语义清晰 |
| 基本类型别名、联合类型、元组 | type | interface 无法实现 |
| 映射类型、条件类型等工具类型 | type | 只有 type 支持类型运算 |
| 简单函数类型定义 | type | 写法更简洁直观 |
| 带属性的函数对象、混合类型 | interface | 调用签名语义更清晰 |
| 类实现接口、面向对象抽象 | interface | 符合面向对象设计语义 |
| 扩展第三方库、全局类型 | interface | 依赖声明合并特性 |
简单总结一句话:对象契约与可扩展场景用 interface,灵活组合与类型运算用 type。
现在 TS 社区的主流趋势是:日常业务开发中 type 的使用频率越来越高,因为它更灵活、覆盖场景更广;而 interface 更多用于公共类型定义、库的类型声明等需要可扩展性的场景