Dark Dwarf Blog background

TypeScript: Type vs. Interface

TypeScript: Type vs. Interface

在 TypeScript 中,typeinterface 是两种定义类型的主要方式。它们在很多情况下功能相似,但在关键方面存在差异。下面系统梳理一下它们的相同点与不同点,以及使用的最佳实践。

1. 相同点

在定义对象的形状或函数签名时,typeinterface 的语法和功能几乎相同。两者都可以用来描述一个对象应该包含哪些属性和方法与定义一个函数的类型签名。。

// 使用 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. 不同点

a.a. 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 对象添加自定义属性。

b.b. 扩展类型语法

两者都可以扩展其他类型,但语法不同。

  • 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 };

c.c. 类型别名的灵活性

type 别名比 interface 更加灵活,它可以用于定义 interface 无法表达的类型。

  1. 原始类型别名: type 可以为任何类型创建别名,包括 string, number 等原始类型。
type UserID = string;
type StatusCode = 200 | 404 | 500;
  1. 联合类型 (Union Types): type 可以轻松定义一个可以是多种类型之一的联合类型。
type StringOrNumber = string | number;
  1. 元组类型 (Tuple Types): type 可以定义一个固定长度和元素类型的数组。
type Point = [number, number];
  1. 映射类型和高级类型: type 是创建映射类型、条件类型等高级类型工具的唯一选择。
// 映射类型:将一个现有类型的所有属性变为可选
type Partial<T> = {
  [P in keyof T]?: T[P];
};

interface 只能用于声明对象的形状,无法实现上述功能。

3. 最佳实践

基于以上差异,社区和 TypeScript 官方文档形成了一些最佳实践:

  1. 优先使用 interface 定义数据结构: 当我们定义一个对象的形状(例如 API 响应、组件的 props)时,优先使用 interface。它的声明合并特性提供了更好的扩展性。

  2. 需要特定功能时使用 type: 当我们需要定义联合类型、元组、原始类型别名,或者使用映射类型等高级类型功能时,type 是你唯一的选择。

开始类型选择

你要定义一个对象的形状吗?

使用 interface

使用 type

你需要扩展一个已有的 interface 吗?

继续使用 interface

结束:使用 interface

你需要联合类型、元组或映射类型吗?

使用 type

你需要在一个 type 别名的基础上添加属性吗?

使用 & 创建新的 type

结束:使用 type

结束:继续使用 interface

结束:使用 type

结束:使用 & 创建新的 type