TypeScript: Type vs. Interface
在 TypeScript 中,type 和 interface 是两种定义类型的主要方式。它们在很多情况下功能相似,但在关键方面存在差异。下面系统梳理一下它们的相同点与不同点,以及使用的最佳实践。
1. 相同点
在定义对象的形状或函数签名时,type 和 interface 的语法和功能几乎相同。两者都可以用来描述一个对象应该包含哪些属性和方法与定义一个函数的类型签名。。
// 使用 interface
interface IUser {
id: number;
name: string;
}
// 使用 type
type TUser = {
id: number;
name: string;
};
const user: IUser = { id: 1, name: "Alice" };
const user2: TUser = { id: 2, name: "Bob" };
// 使用 interface
interface IGreeter {
(name: string): string;
}
// 使用 type
type TGreeter = (name: string) => string;
const greet: IGreeter = (name) => `Hello, ${name}`;
const greet2: TGreeter = (name) => `Hi, ${name}`;
2. 不同点
interface 特性
声明合并是 interface 的独有特性:
interface Box {
height: number;
width: number;
}
// 在其他地方,或者为了扩展,再次声明 Box
interface Box {
scale: number;
}
// 合并后的 Box 接口拥有 height, width, 和 scale 三个属性
const box: Box = { height: 5, width: 6, scale: 10 };
type 别名则不允许重名,这样做会直接导致编译错误。
type Window = {
title: string;
};
// 错误: Duplicate identifier 'Window'.
type Window = {
ts: any;
};
适用场景: 这个特性使得
interface非常适合在需要扩展第三方库类型定义或模块化地增强现有接口时使用。例如,为全局的window对象添加自定义属性。
扩展类型语法
两者都可以扩展其他类型,但语法不同。
interface使用extends关键字,这与许多面向对象语言中的类继承非常相似,语义清晰。type使用交叉类型 (&) 来合并类型。
// Interface 使用 extends
interface Animal {
name: string;
}
interface Bear extends Animal {
honey: boolean;
}
const bear: Bear = { name: "Winnie", honey: true };
// Type 使用交叉类型 &
type Shape = {
color: string;
};
type Circle = Shape & {
radius: number;
};
const circle: Circle = { color: "blue", radius: 5 };
类型别名的灵活性
type 别名比 interface 更加灵活,它可以用于定义 interface 无法表达的类型。
- 原始类型别名:
type可以为任何类型创建别名,包括string,number等原始类型。
type UserID = string;
type StatusCode = 200 | 404 | 500;
- 联合类型 (Union Types):
type可以轻松定义一个可以是多种类型之一的联合类型。
type StringOrNumber = string | number;
- 元组类型 (Tuple Types):
type可以定义一个固定长度和元素类型的数组。
type Point = [number, number];
- 映射类型和高级类型:
type是创建映射类型、条件类型等高级类型工具的唯一选择。
// 映射类型:将一个现有类型的所有属性变为可选
type Partial<T> = {
[P in keyof T]?: T[P];
};
interface 只能用于声明对象的形状,无法实现上述功能。
3. 最佳实践
基于以上差异,社区和 TypeScript 官方文档形成了一些最佳实践:
-
优先使用
interface定义数据结构: 当我们定义一个对象的形状(例如 API 响应、组件的 props)时,优先使用interface。它的声明合并特性提供了更好的扩展性。 -
需要特定功能时使用
type: 当我们需要定义联合类型、元组、原始类型别名,或者使用映射类型等高级类型功能时,type是你唯一的选择。