TypeScript 必考题:Type 与 Interface 全面对比
在 TypeScript 中,interface 和 type 都是用来定义类型的核心工具------告诉编译器"这个变量应该长什么样"。简单来说:
interface(接口)定义的是一个"契约",专注于描述对象的形状(shape):它该有哪些属性、哪些方法,分别是什么类型。type(类型别名)则是给任意类型起一个名字------不限于对象,还可以是基本类型、联合类型、元组、函数签名,甚至是它们的任意组合。
一句话概括:interface 说的是"这个对象必须满足的结构",type 说的是"你可以用这个名字来引用这个类型"。
二者看起来都能描述对象结构,很多人便以为它们可以无脑互换。也正因为如此,type 和 interface 的区别成了 TypeScript 面试中绕不开的高频题。下面就沿着共同点 → 核心差异 → 实践建议这条线,把这道题彻底讲透。
一、共同点:都能描述对象的结构
最基础的用法上,二者几乎一致------描述一个对象的属性结构:
typescript
// interface 写法
interface User {
name: string;
age: number;
avatarURL: string;
}
// type 写法
type UserType = {
name: string;
age: number;
avatarURL: string;
};
// 使用方式完全相同
const u1: User = {
name: "张三",
age: 18,
avatarURL: "https://example.com/avatar.jpg",
};
const u2: UserType = {
name: "李四",
age: 19,
avatarURL: "https://example.com/avatar.jpg",
};
无论是 interface 还是 type,编译器都能正确进行类型检查。这也是初学者最容易产生"二者可互换"错觉的地方------但接下来你就会看到,它们的分歧远比共同点多。
二、核心差异
1. 继承方式:extends vs &
当需要基于已有类型扩展出新类型时,两者的语法截然不同:
typescript
// interface:使用 extends 继承
interface Person {
name: string;
}
interface Student extends Person {
job: string;
}
const s1: Student = {
name: "张三",
job: "前端开发",
};
// type:使用 & 交叉类型
type PersonType = { name: string };
type EmployeeType = PersonType & { job: string };
const e1: EmployeeType = {
name: "李四",
job: "前端开发",
};
interface 的 extends 带着面向对象"继承"的语义,读起来非常直观。而 type 用 &(交叉类型)来组合,更像是函数式的"拼装"------你可以把任意多个类型合并到一起,灵活性更强。
2. 声明合并:interface 的独门绝技
这是面试官最爱追问的一点。
typescript
// interface:两次定义同名接口,会自动合并
interface Animal {
name: string;
}
interface Animal {
age: number;
}
const dog: Animal = {
// 两个 Animal 的成员合并到了一起
name: "旺财",
age: 1,
};
// type:两次定义同名类型直接报错!
type AnimalType = { name: string };
type AnimalType = { age: number }; // ❌ 标识符"AnimalType"重复
背后的原因在于,TypeScript 特意为 interface 设计了声明合并(Declaration Merging) 机制。它让你可以在不修改原始代码的前提下,对第三方库或全局类型打"扩展补丁"------比如给 Window 对象追加自定义属性,这在大型项目中相当实用。而 type 作为类型别名,本质上是一个唯一的引用,自然不允许被重复声明。
3. 非对象类型:type 的绝对优势
type 能表达的类型范围远超 interface:
typescript
// ✅ 联合类型
type ID = number | string;
// ✅ 元组类型
type Point = [number, number];
// ❌ interface 做不到
当你需要的不是一个单纯的对象形状------比如"可以是数字也可以是字符串"的联合类型,或者固定长度的元组------type 是唯一的选择。interface 天生就是围绕对象结构设计的,它的能力边界也止步于此。
4. 函数类型约束:两种写法,一个效果
二者都能描述函数签名,只是风格不同:
typescript
// interface:调用签名
interface AddFn {
(a: number, b: number): number;
}
const add: AddFn = (a, b) => a + b;
add(1, 2);
// type:箭头函数风格
type AddFnType = (a: number, b: number) => number;
const addType: AddFnType = (a, b) => a + b;
addType(1, 2);
interface 的写法暗示"这是一个可被调用的对象",而 type 的箭头风格更简洁直白。功能上没有高下之分,选择哪种取决于个人和团队的编码偏好。
三、一目了然:总结对比
| 维度 | interface |
type |
|---|---|---|
| 描述对象结构 | ✅ | ✅ |
| 继承 / 扩展 | extends |
& 交叉类型 |
| 声明合并 | ✅ 同名自动合并 | ❌ 重复声明报错 |
| 联合类型 | ❌ | ✅ |
| 元组类型 | ❌ | ✅ |
| 函数类型 | 调用签名语法 | 箭头函数语法 |
| 库类型补丁 | 强(声明合并) | 弱 |
四、实践建议
实际开发中没有绝对的"对"与"错",但社区沉淀出了一些公认的经验:
- 优先用
interface描述对象、类、组件 Props------它的语法更聚焦,编译器的错误提示也更清晰。 - 用
type处理联合类型、交叉类型、元组、映射类型等interface表达不了的场景。 - 对外暴露的类型 (API、库),首选
interface------方便使用者通过声明合并进行扩展。 - 比纠结细节更重要的是团队统一风格------选一个规范,然后坚持执行。
把这几个核心差异吃透,TypeScript 这道 type vs interface 的面试题,你就能从容应对了。