TypeScript 中 Type 和 Interface 傻傻分不清?看完这篇就不纠结了

你是不是也有过这样的困惑:

打开一个老项目,或者在做 Code Review 时,发现代码里一会儿是 interface Props,一会儿又是 type State。问同事为什么要混用,他也支支吾吾说不出个所以然,最后只能来一句:"哎呀,反正都能跑,看心情呗。"

但在 TypeScript 的世界里,"能跑"和"写得好"是两码事。

type(类型别名)和 interface(接口)这对双胞胎,在 TS 诞生之初就一直相爱相杀。它们确实太像了,像到在绝大多数 CRUD 业务开发中,你闭着眼随便选一个都不会报错。

但是,作为一个追求代码质量的开发者,我们不能止步于此。

今天,我们跳出表面的语法糖,从底层机制入手,彻底搞清楚它们的本质区别,并给你一套最佳实践方案。拒绝选择困难症。

1. 表象:90% 的重合度与其误区

为什么大家会纠结?因为在定义"对象"的形状(Shape)时,它们长得几乎一模一样。

看看下面的代码,你能一眼看出区别吗?

typescript 复制代码
// 使用 interface
interface UserI {
  name: string;
  age: number;
}

// 使用 type
type UserT = {
  name: string;
  age: number;
}

在日常使用中,如果你想实例化一个对象,或者在函数参数中约束类型,这俩兄弟的表现是完全一致的。它们都支持:

  • 定义对象结构
  • 定义函数签名
  • 支持泛型
  • 支持类(Class)的实现(implements)

但这正是新手容易陷入的误区:以为它们是可以随意互换的同义词。 实际上,TS 设计这两个概念,是为了解决完全不同的问题。

2. 核心:声明合并、类型表达与扩展性

区别不仅存在,而且在关键时刻决定了你的架构设计是否合理。主要体现在以下三个核心维度:

2.1 声明合并 (Declaration Merging) ------ Interface 的必杀技

这是 interface 独有的特性,也是它存在的最大理由。

场景模拟:

你引入了一个第三方库(比如 Vue 或 jQuery),但你发现它的全局对象上少了一个你需要的属性。这时,如果你用 interface,你可以直接在自己的代码里"补"上这个属性。

kotlin 复制代码
// 假设这是第三方库定义的 interface
interface User {
  name: string;
}

// 你的代码中再次定义同名 interface
interface User {
  age: number;
}

// ✨ TS 会自动把它们缝合在一起!
const me: User = {
  name: "Gemini",
  age: 18 // 必须两个属性都有,否则报错
};

反观 type:

它是封闭的(Closed)。一旦定义,无法通过同名方式修改。

ini 复制代码
type User = {
  name: string;
};

// ❌ 报错:Duplicate identifier 'User'.
type User = {
  age: number;
};

💡 结论: interface 具有开放性,允许后续扩展;而 type 具有封闭性,更适合确定的业务逻辑。

2.2 类型表达能力 ------ Type 的主场

type 的全称是 Type Alias(类型别名) 。既然是别名,它就能给任何东西起名字,不仅仅是对象。

在处理复杂类型时,Type 的灵活性完胜 Interface:

  • 联合类型 (Union Types): 前端开发中最常用的功能。

    ini 复制代码
    type Status = 'pending' | 'success' | 'failed';
    type ID = string | number;

    Interface 无法直接定义这种"或"的关系。

  • 元组 (Tuple):

    ini 复制代码
    type Point = [number, number];
  • 类型体操:

    当你使用 Pick、Omit、Record 或者条件类型(Conditional Types)时,产出的结果通常都是 type。

2.3 扩展方式:Extends vs Intersection

虽然两者都能实现"继承"的效果,但语义不同。

  • Interface 使用 extends:侧重于面向对象的层级继承。
  • Type 使用 & (交叉类型) :侧重于集合的合并。

虽然通常可以互通,但在处理冲突属性时,interface 会直接报错提醒,而交叉类型(&)可能会产生 never 类型,导致错误提示不够直观。


3. 规范:一套拿来即用的最佳实践

讲了这么多理论,回到最初的问题:我们在项目中到底该怎么选?

与其每次都纠结,不如遵循这套简单的 "二选一法则" ,这也符合目前主流大厂(如 Google 规范)和 React 社区的推荐趋势:

场景一:你在编写库 (Library) 或第三方包

请优先使用 interface

理由: 作为库的作者,你需要为你的用户留出"后路"。用户可能需要利用"声明合并"的特性,向你的全局接口中注入自定义属性(比如扩展 Window 对象或给 Request 对象增加 user 字段)。使用 Interface 是对使用者的尊重。

场景二:你在编写业务应用 (Application / UI 组件)

请优先使用 type

理由:

  1. 一致性 (Consistency): 既然 type 能搞定对象、联合类型、元组等所有情况,而 interface 只能搞定对象,那么全员使用 type 可以让代码风格更统一。
  2. 安全性 (Safety): 在业务代码中,我们通常不希望定义好的类型被莫名其妙地"自动合并"了(这是隐患)。type 的报错提醒能让你更安全。
  3. React 生态: 现在的 React 社区更倾向于用 type 来定义 PropsState,因为它在处理组件复合类型时更加直观。

总结

为了方便记忆,我做了一张对比速查表:

特性 Interface Type
核心理念 描述对象的形状 (Shape) 任何类型的别名 (Alias)
声明合并 支持 (自动合并) ❌ 不支持 (会报错)
**联合类型 ( )** ❌ 不支持
映射/条件类型 ❌ 不支持 ✅ 支持
最佳使用场景 编写库 (Library) 编写应用 (App)

一句话口诀:

对外 API(库)用 Interface,对内业务逻辑用 Type。如果你实在拿不准,就用 Type,直到你必须用 Interface 为止。

你们团队的代码规范里,是强制用 type 还是 interface?还是像大部分项目一样"随缘混用"?

欢迎在评论区留言,我们一起聊聊 TS 里的那些坑!

相关推荐
GISer_Jing12 小时前
WebGL跨端兼容实战:移动端适配全攻略
前端·aigc·webgl
迦南giser12 小时前
前端性能——传输优化
前端
小白_ysf12 小时前
Vue 中常见的加密方法(对称、非对称、杂凑算法)
前端·vue.js·算法
人工智能训练19 小时前
【极速部署】Ubuntu24.04+CUDA13.0 玩转 VLLM 0.15.0:预编译 Wheel 包 GPU 版安装全攻略
运维·前端·人工智能·python·ai编程·cuda·vllm
会跑的葫芦怪19 小时前
若依Vue 项目多子路径配置
前端·javascript·vue.js
pas1361 天前
40-mini-vue 实现三种联合类型
前端·javascript·vue.js
摇滚侠1 天前
2 小时快速入门 ES6 基础视频教程
前端·ecmascript·es6
珑墨1 天前
【Turbo】使用介绍
前端
军军君011 天前
Three.js基础功能学习十三:太阳系实例上
前端·javascript·vue.js·学习·3d·前端框架·three
打小就很皮...1 天前
Tesseract.js OCR 中文识别
前端·react.js·ocr