在 TypeScript 开发中,interface(接口)和 type(类型别名)是定义类型的两种核心方式。虽然它们功能相似,但在使用细节和适用场景上存在关键区别,深刻理解这些区别有助于我们编写出更健壮、更易于维护的类型代码。本文将详细对比两者的六大差异,并给出实际场景下的选择建议。
一、可定义的类型范围不同
这是两者最基础的区别,决定了它们的应用广度。
interface:主要用于定义对象或类的形状(即结构类型)。它无法直接用来定义基本类型别名、联合类型或元组等。
type:功能更为广泛,可以定义任意类型,包括基本类型别名、联合类型、交叉类型、元组以及函数类型等。
// type 可以定义基本类型别名
type UserName = string;
// type 可以定义联合类型
type ID = string | number;
// type 可以定义元组类型
type DataTuple = [string, number];
// interface 只能定义对象或类的结构
interface User {
name: string;
age: number;
}
下图直观展示了二者在类型定义范围上的差异:


二、扩展与继承的方式不同
当需要在一个已有类型的基础上创建新类型时,两者采用的语法截然不同。
interface:使用 extends 关键字进行继承扩展。它只能扩展其他 interface 或对象类型的 type。
type:使用交叉类型(&)来实现类型的组合。这种方式可以合并任意类型,不仅限于对象类型。
// interface 使用 extends 扩展
interface Animal {
name: string;
}
interface Dog extends Animal {
bark(): void;
}
// type 使用交叉类型(&)进行扩展
type Animal = { name: string };
type Dog = Animal & { bark(): void };


三、声明合并(Declaration Merging)
这是 interface 独有的特性,也是其与 type 的一个显著区别。
interface:支持同名接口自动合并。这意味着你可以多次定义同名的 interface,TypeScript 编译器会将它们的所有声明合并为一个。
type:不允许同名类型别名重复定义,如果尝试重复定义,编译器会直接报错。
// interface 支持声明合并
interface User {
name: string;
}
interface User {
age: number;
}
// 合并后的 User 接口包含 name 和 age 属性
const user: User = { name: "Tom", age: 18 };
// type 不允许重复定义,以下代码会报错
type User = { name: string };
type User = { age: number }; // Error: Duplicate identifier 'User'
这个特性使得 interface 在扩展第三方库类型或全局类型定义时非常有用。
四、函数类型的定义方式
两者都可以用来描述函数类型,但语法上有所偏好。
interface:通过调用签名(Call Signature)来定义。
type:通常使用更简洁的箭头函数语法直接定义。
// 使用 interface 定义函数类型
interface SearchFunc {
(source: string, subString: string): boolean;
}
// 使用 type 定义函数类型
type SearchFunc = (source: string, subString: string) => boolean;
五、类实现(implements)的限制
在面向对象编程中,类可以实现(implements)某个接口或类型,但 type 在此处有更多限制。
interface:可以被类直接 implements。
type:只有当其定义的是一个纯粹的对象结构时,才能被类实现。如果 type 是联合类型、交叉类型等复杂类型,则无法被类实现。
// interface 可以被类实现
interface Shape {
area(): number;
}
class Circle implements Shape {
area() { return Math.PI * 5 ** 2; }
}
// 对象类型的 type 也可以被实现
type Shape = { area(): number };
class Square implements Shape {
area() { return 10 * 10; }
}
// 包含联合类型的 type 无法被类实现
type Shape = { area(): number } | { perimeter(): number };
class Triangle implements Shape { // Error: 无法将联合类型作为类实现的接口
area() { return 0.5 * 10 * 5; }
}
六、对映射类型的支持
映射类型(Mapped Types)是 TypeScript 中一种强大的类型编程工具,用于基于旧类型创建新类型。
type:天然支持映射类型。许多内置工具类型(如 Partial<T>, Readonly<T>, Pick<T, K>)本身就是用 type 和映射类型语法实现的。
interface:不支持直接使用映射类型的语法来定义一个新的 interface。
// 使用 type 创建映射类型
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type User = { name: string; age: number };
type ReadonlyUser = Readonly<User>; // 结果为 { readonly name: string; readonly age: number; }
理解映射类型对于构建复杂的类型系统和实现高级类型安全至关重要。
如何选择:interface 还是 type?
在实际开发中,选择哪一种并没有绝对的对错,但遵循一些共识可以使代码更清晰、协作更顺畅。
-
优先使用 interface 的情况:
- 当你需要定义对象或类的结构,并且未来可能通过声明合并进行扩展(例如为第三方库添加类型定义)。
- 在面向对象的程序设计场景中,使用
interface 来定义契约,代码意图更清晰,更符合传统 OOP 的习惯。
-
优先使用 type 的情况:
- 需要定义基本类型别名、联合类型、元组类型或函数类型时(
interface 无法直接做到)。
- 需要使用映射类型、条件类型等高级类型编程特性时。
- 需要通过交叉类型(
&)来组合多个现有类型(尤其是非对象类型)时。
总结来说,对于简单的对象类型定义,两者可以互换,社区也倾向于使用 interface 以获得更好的扩展性。而对于需要类型组合、定义非对象类型或进行类型编程的复杂场景,type 是唯一的选择。掌握这些区别能帮助你在不同的系统设计需求中做出最合适的选择。