TypeScript 必考题:Type 与 Interface 全面对比

29 阅读4分钟

TypeScript 必考题:Type 与 Interface 全面对比

在 TypeScript 中,interface 和 type 都是用来定义类型的核心工具——告诉编译器"这个变量应该长什么样"。简单来说:

  • interface(接口)定义的是一个"契约",专注于描述对象的形状(shape):它该有哪些属性、哪些方法,分别是什么类型。
  • type(类型别名)则是给任意类型起一个名字——不限于对象,还可以是基本类型、联合类型、元组、函数签名,甚至是它们的任意组合。

一句话概括:interface 说的是"这个对象必须满足的结构",type 说的是"你可以用这个名字来引用这个类型"。

二者看起来都能描述对象结构,很多人便以为它们可以无脑互换。也正因为如此,type 和 interface 的区别成了 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 &

当需要基于已有类型扩展出新类型时,两者的语法截然不同:

// 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 的独门绝技

这是面试官最爱追问的一点。

// 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:

// ✅ 联合类型
type ID = number | string;

// ✅ 元组类型
type Point = [number, number];

// ❌ interface 做不到

当你需要的不是一个单纯的对象形状——比如"可以是数字也可以是字符串"的联合类型,或者固定长度的元组——type 是唯一的选择。interface 天生就是围绕对象结构设计的,它的能力边界也止步于此。


4. 函数类型约束:两种写法,一个效果

二者都能描述函数签名,只是风格不同:

// 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 的箭头风格更简洁直白。功能上没有高下之分,选择哪种取决于个人和团队的编码偏好。


三、一目了然:总结对比

维度interfacetype
描述对象结构✅✅
继承 / 扩展extends& 交叉类型
声明合并✅ 同名自动合并❌ 重复声明报错
联合类型❌✅
元组类型❌✅
函数类型调用签名语法箭头函数语法
库类型补丁强(声明合并)弱

四、实践建议

实际开发中没有绝对的"对"与"错",但社区沉淀出了一些公认的经验:

  • 优先用 interface 描述对象、类、组件 Props——它的语法更聚焦,编译器的错误提示也更清晰。
  • 用 type 处理联合类型、交叉类型、元组、映射类型等 interface 表达不了的场景。
  • 对外暴露的类型(API、库),首选 interface——方便使用者通过声明合并进行扩展。
  • 比纠结细节更重要的是团队统一风格——选一个规范,然后坚持执行。

把这几个核心差异吃透,TypeScript 这道 type vs interface 的面试题,你就能从容应对了。