Skip to content

第 2 章:本质区别 —— 名义类型 vs 结构类型

在上一章中,我们讨论了传统"标准答案"的局限性。现在,让我们深入了解 interfacetype 的本质区别:名义类型 vs 结构类型。

TypeScript 的结构类型系统

TypeScript 采用的是结构类型系统(Structural Type System),而不是名义类型系统(Nominal Type System)。这意味着两个类型的兼容性是由它们的结构决定的,而不是由它们的名称决定的。

让我们看一个例子:

typescript
interface User {
  name: string;
  age: number;
}

interface Customer {
  name: string;
  age: number;
}

const user: User = { name: "Alice", age: 30 };
const customer: Customer = user; // 这是可以的!

在这个例子中,尽管是不同的接口,但由于它们具有相同的结构,TypeScript 认为它们是兼容的。

相比之下,在名义类型系统(如 Java)中,即使两个类有完全相同的属性和方法,它们也不能互相赋值,除非显式声明继承关系。

interface 是"开放契约"

interface 在 TypeScript 中被设计为"开放契约",这意味着它可以被多次声明并且会自动合并:

typescript
interface Logger {
  log(message: string): void;
}

// 在另一个文件中扩展同一个接口
interface Logger {
  warn(message: string): void;
}

// 最终效果相当于:
// interface Logger {
//   log(message: string): void;
//   warn(message: string): void;
// }

const logger: Logger = {
  log(message) { console.log(message); },
  warn(message) { console.warn(message); }
};

这种特性使得我们可以扩展第三方库的类型定义,或者在一个大型项目中逐步增强接口的功能。

type 是"封闭别名"

interface 不同,type 创建的是一个"封闭别名",一旦定义就不能再扩展:

typescript
type User = {
  name: string;
  age: number;
};

// 这样做会报错!
// type User = {
//   email: string;
// };

type 更像是一种类型级别的变量,给复杂类型起了一个简短的名字。

实际应用中的选择策略

了解了这两种本质区别后,我们可以形成一个新的选择策略:

  1. 如果你需要定义一个可以被扩展的"契约",特别是在面向对象的设计中,使用 interface
  2. 如果你只是想给复杂类型起个别名,或者定义联合类型、元组等,使用 type

示例:第三方库扩展

当你使用第三方库时,经常需要扩展库中定义的类型:

typescript
// 第三方库定义
interface Config {
  host: string;
  port: number;
}

// 你的项目想要添加额外配置项
interface Config {
  timeout?: number;
  retries?: number;
}

这种情况非常适合使用 interface,因为它支持声明合并。

示例:内部数据结构

对于内部使用的数据结构,特别是 DTO(Data Transfer Object),通常更适合使用 type

typescript
type UserDTO = {
  id: number;
  name: string;
  email: string;
};

// 或者联合类型
type Status = 'pending' | 'approved' | 'rejected';

小结

在本章中,我们深入探讨了 interfacetype 的本质区别:

  • TypeScript 采用结构类型系统,不关心类型的名称,只关心结构
  • interface 是"开放契约",支持声明合并
  • type 是"封闭别名",强调不可变性

下一章,我们将探讨面向对象编程中如何更好地使用 interface,包括类的契约、继承体系等内容。


思考题: 你能想到哪些场景特别适合使用 interface 的声明合并特性?又有哪些场景更适合使用 type 的不可变性?