TypeScript 面试必考:type 与 interface 到底有什么区别?
"请你说说 TypeScript 中
type和interface的区别?"——这道题几乎出现在每一场前端面试中。很多人能说出一两点,但很难答全、答深。本文从面试角度系统梳理,帮你一次讲清楚。
目录
一、先看共同点:它们都能做什么?
在讨论区别之前,先明确 type 和 interface 的共同能力,这是面试中容易被忽略的加分项。
1. 都可以描述对象结构
// interface 描述对象
interface User {
name: string;
age: number;
avatarUrl: string;
}
// type 描述对象
type UserType = {
name: string;
age: number;
avatarUrl: string;
};
// 用法完全一致
const u1: User = {
name: 'zwq',
age: 18,
avatarUrl: 'https://zwq.ai/avatar.png',
};
const u2: UserType = {
name: 'zwq',
age: 18,
avatarUrl: 'https://zwq.ai/avatar.png',
};
2. 都可以用于函数参数和返回值类型约束
function greet(user: User): string {
return `Hello, ${user.name}`;
}
function greet2(user: UserType): string {
return `Hello, ${user.name}`;
}
3. 都支持 readonly 修饰符
interface Config {
readonly apiUrl: string;
}
type ConfigType = {
readonly apiUrl: string;
};
面试话术:
type和interface在描述对象结构时,能力是完全重叠的。选择哪个更多是团队规范问题,而非功能限制。
二、核心区别逐条拆解
区别一:继承方式不同
这是面试中最常问的第一个点。
interface 使用 extends 继承:
interface Person {
name: string;
}
interface Employee extends Person {
job: string;
}
const e1: Employee = {
name: '张三',
job: '前端工程师',
};
type 使用交叉类型 & 实现组合:
type PersonType = {
name: string;
};
type EmployeeType = PersonType & {
job: string;
};
const e2: EmployeeType = {
name: '李四',
job: '后端工程师',
};
面试追问:
interface能继承type吗? 答:可以!只要type描述的是对象结构,interface就能extends它。反过来,type也能用&组合interface。两者在对象类型上是互通的。
interface Animal extends PersonType {
species: string;
}
type WorkerType = EmployeeType & {
company: string;
};
⚠️ 关键区分:extends 与 & 在属性冲突时的行为差异
这是面试中拉开差距的深度追问点:
interface A { x: number }
interface B extends A { x: string } // ❌ 编译报错:类型不兼容
type C = A & { x: string } // ✅ 不报错,但 x 的类型变为 never
面试要点:
extends是严格的类型检查,属性冲突直接报错;&是类型合并,冲突时会得到never(编译通过但无法使用)。实际开发中&的这种"静默失败"更危险,因为它不会在编译期提醒你。
区别二:声明合并(Declaration Merging)
这是 interface 独有的能力,也是面试高频考点。
interface 可以重复声明,自动合并:
interface Animal {
name: string;
}
// 再次声明同名 interface,属性会自动合并
interface Animal {
age: number;
}
// 最终 Animal = { name: string; age: number }
const dog: Animal = {
name: '旺财',
age: 3,
};
type 不允许重复声明:
type AnimalType = {
name: string;
};
// ❌ 编译报错:Duplicate identifier 'AnimalType'
type AnimalType = {
age: number;
};
面试追问:声明合并有什么实际应用场景? 答:
- 扩展第三方库类型:比如给
Window对象添加自定义属性- 模块化类型定义:不同文件中分批定义同一个接口
- 库的类型声明:很多 npm 包的
@types定义大量依赖声明合并
// 扩展 Window 类型(实际开发高频场景)
declare global {
interface Window {
__APP_CONFIG__: {
apiUrl: string;
env: 'dev' | 'prod';
};
}
}
// 使用时就有类型提示了
const apiUrl = window.__APP_CONFIG__.apiUrl;
区别三:type 能表示非对象类型
这是 type 最大的优势——它的表达能力更广。
type 可以定义联合类型:
type ID = string | number;
// interface 没有联合类型的语法,只能描述对象结构
type 可以定义元组类型:
type Point = [number, number];
const p: Point = [10, 20];
type 可以定义基本类型别名:
type Status = 'loading' | 'success' | 'error';
type Age = number;
type Nullable<T> = T | null;
面试话术:
type是"类型别名",可以为任何类型起别名;interface专注于描述对象结构。当需要联合类型、元组、基本类型别名时,只能用type。
区别四:函数类型与索引签名
函数类型的写法
两者都能定义函数类型,但 type 的写法更简洁。
interface 定义函数类型(调用签名):
interface AddFn {
(a: number, b: number): number;
}
const add1: AddFn = (x, y) => {
return x + y;
};
type 定义函数类型:
type AddType = (a: number, b: number) => number;
const add2: AddType = (x, y) => {
return x + y;
};
面试对比:
type用箭头语法=>直接定义,更符合我们对函数类型的直觉认知;interface需要用调用签名的形式,写法更繁琐。实际开发中函数类型推荐用type。
索引签名
两者都支持索引签名,写法一致:
interface StringMap {
[key: string]: string;
}
type StringMapType = {
[key: string]: string;
};
注意:
interface还支持构造签名(new),这在定义类的构造函数类型时有用,但日常开发中较少用到。
三、一张表总结
| 特性 | interface | type |
|---|---|---|
| 描述对象结构 | ✅ | ✅ |
| 继承/组合 | extends(冲突时报错) | &(冲突时 never) |
| 声明合并 | ✅ 自动合并 | ❌ 重复报错 |
| 联合类型 | ❌ | ✅ string | number |
| 元组类型 | ❌ | ✅ [number, string] |
| 基本类型别名 | ❌ | ✅ type Age = number |
| 函数类型 | ✅ 调用签名 | ✅ 箭头语法(更简洁) |
| 索引签名 | ✅ | ✅ |
implements/extends | ✅ 类可实现 | ✅(对象类型时) |
| 映射类型/条件类型 | ❌ | ✅ type 独有 |
四、面试标准回答模板
当面试官问"说说 type 和 interface 的区别"时,建议按以下结构回答:
📋 点击展开完整回答模板
先说共同点:两者都可以描述对象结构,用于类型约束,能力在对象类型上是重叠的。
再说核心区别:
- 继承方式不同——
interface用extends,type用&交叉类型。而且extends是严格检查,属性冲突直接报错;&是合并,冲突时得到never- 声明合并——
interface支持同名自动合并,type不允许重复声明。这在扩展第三方库类型时很有用- 类型表达范围——
type可以定义联合类型、元组、基本类型别名,interface只能描述对象结构- 函数类型——都能定义,但
type的箭头语法更简洁直观最后说选择建议:描述对象结构优先用
interface(可扩展、可合并),需要联合类型或工具类型时用type。团队统一规范最重要。
五、进阶:实际开发中怎么选?
| 场景 | 推荐 | 原因 |
|---|---|---|
| 定义 Props、State、API 响应 | interface | 可被 extends 扩展,支持声明合并 |
| 联合类型、字面量类型 | type | interface 做不到 |
工具类型(Pick、Omit 等) | type | 类型运算必须用 type |
| 函数类型 | type | 写法更简洁 |
需要被 class implements | interface | 语义更清晰 |
💡 真实项目中的经验法则:React 项目里,组件 Props 用
interface(方便扩展),工具函数的入参出参用type(写法简洁)。两者混用是正常的,关键是团队统一。
六、面试加分项
如果你能主动提到以下几点,会是很好的加分项:
1. 声明合并的实际价值
扩展 Window、Express.Request 等全局类型是声明合并最典型的实战场景。面试时举这个例子说明你有实际项目经验。
2. type 的映射类型和条件类型
这是 type 独有的高级能力,interface 无法实现:
// type 独有的映射类型
type Readonly<T> = { readonly [K in keyof T]: T[K] };
type Partial<T> = { [K in keyof T]?: T[K] };
// type 独有的条件类型
type IsString<T> = T extends string ? 'yes' : 'no';
// interface 无法做到
// interface Readonly<T> { readonly [K in keyof T]: T[K] } // ❌ 语法错误
3. 编译性能
interface 通常被认为在类型检查时有一定性能优势(声明式、支持延迟求值),但 TypeScript 官方未给出明确对比结论。实际差异在多数项目中可忽略不计,不必过度纠结。
4. 社区趋势
React 社区倾向用 interface 定义 Props(Airbnb、Google 的 TS 规范都是如此),TypeScript 工具库倾向用 type。了解这些有助于你在面试中展现对社区生态的关注。
📌 一句话总结:
interface是"对象的契约",type是"类型的别名"。对象结构用interface,类型运算用type,面试时把这个逻辑讲清楚就稳了。
你在面试中被问到过这个问题吗?你平时写 TypeScript 更习惯用 type 还是 interface?欢迎在评论区聊聊你的选择和理由 👇