Skip to content

第 1 章:从八股文说起 —— 那些年我们背过的"标准答案"

大家好,欢迎来到《TypeScript 中 type 和 interface 的本质区别与真实项目应用》专栏的第一章。我是你们的作者,今天我们要聊的话题可能会打破一些你习以为常的认知。

那些年我们背过的"标准答案"

还记得第一次学习 TypeScript 时,你是怎么区分 typeinterface 的吗?

大多数教程都会告诉我们这样的"标准答案":

  • "interface 可以合并,type 不行"
  • "type 适合联合类型,interface 不行"
  • "能用 interface 就用 interface"

这些说法本身没有错,但问题在于——它们只是一些孤立的规则,而不是指导原则。在真实的项目开发中,你会发现这些"标准答案"远远不够用。

规则的局限性

让我们来看几个真实项目中的例子:

例子 1:React 组件 Props

typescript
// 有人这样定义组件 props
interface ButtonProps {
  text: string;
  onClick: () => void;
}

// 也有人这样定义
type ButtonProps = {
  text: string;
  onClick: () => void;
}

按照"能用 interface 就用 interface"的标准答案,我们应该选择第一种写法。但在 React 生态中,第二种写法其实更常见。为什么?

例子 2:API 响应类型

typescript
// API 返回的数据类型
interface UserResponse {
  id: number;
  name: string;
  email: string;
}

// 业务层使用的用户类型
interface User {
  id: number;
  name: string;
  email: string;
}

如果这两个定义在不同文件中,TypeScript 的结构化类型系统会认为它们是兼容的。但如果其中一个被扩展了,就可能出现意料之外的行为。

例子 3:第三方库扩展

typescript
// 第三方库定义
interface RequestConfig {
  url: string;
  method: string;
}

// 你的项目想添加新功能
interface RequestConfig {
  timeout?: number;
}

这种声明合并确实有用,但也可能导致命名冲突或者意外覆盖。

为什么"标准答案"不够用?

这些问题的根源在于,传统的"标准答案"只关注语法层面的区别,而忽略了更重要的设计考量:

  1. 语义表达:你想表达的是什么?是一种契约还是一种别名?
  2. 演化能力:这个类型未来可能如何变化?
  3. 使用场景:是在定义类的契约,还是在描述数据结构?
  4. 团队约定:你的团队倾向于哪种风格?

重新思考类型设计

与其死记硬背那些规则,不如从更高的层面来思考:

  • interface 更像是面向对象编程中的"契约",它描述了一个对象应该具备哪些能力和行为
  • type 更像是函数式编程中的"别名",它给一个复杂的类型起了一个简单的名字

但这也不是绝对的。随着 TypeScript 的发展,特别是 satisfies 操作符的出现,两者的能力边界越来越模糊,选择更多地取决于团队约定和项目上下文。

小结

在这一章中,我们探讨了传统"标准答案"的局限性,并通过几个实际例子展示了仅凭语法规则无法解决真实项目中的类型设计问题。

下一章,我们将深入探讨 interfacetype 的本质区别:名义类型 vs 结构类型,这将为我们后续的选择提供理论基础。


思考题: 你在项目中有没有遇到过因为 typeinterface 选择不当而导致的问题?欢迎在评论区分享你的经历。