# TypeScript: `interface` vs `type` 终极指南
> 面向全栈工程实践(Node.js + React),提供可落地的选择标准与团队约定。
---
## 一、一句话速记
> ✅ **对象结构 / API 契约 → `interface`**
> ✅ **组合、变换、联合、工具类型 → `type`**
更口语化:
- **"这是个东西" → `interface`**
- **"这是种关系 / 变形" → `type`**
---
## 二、决策表(速查)
| 场景 | 推荐 | 原因 |
|---|---|---|
| 定义对象 / DTO / VO / Props | ✅ `interface` | 语义清晰、可扩展 |
| 定义 API 请求 / 响应结构 | ✅ `interface` | 贴近契约、易维护 |
| 定义函数类型 | ✅ `type` | 简洁、可读性好 |
| 联合类型 / 交叉类型 | ✅ `type` | `interface` 不支持 |
| 字面量联合(替代 enum) | ✅ `type` | 唯一选择 |
| 可辨识联合(Discriminated Union) | ✅ `type` | 事实标准 |
| 扩展第三方类型(声明合并) | ✅ `interface` | `type` 做不到 |
| 工具类型 / 条件类型 / 映射类型 | ✅ `type` | `interface` 做不到 |
| React Props(简单对象) | ✅ `interface` | 社区主流 |
| React Props(复杂联合) | ✅ `type` | 必须 |
---
## 三、本质差异
### 3.1 `interface`:面向"对象结构"
- 语义:**"实现这个契约"**
- 设计目标:OOP、继承、扩展
- 特点:
- ✅ 声明合并(Declaration Merging)
- ✅ `extends` 继承
- ✅ 更适合人类阅读
```ts
interface User {
id: number;
name: string;
}
```
> 读起来像:"User 是一个拥有 id 和 name 的对象"
---
### 3.2 `type`:面向"类型关系"
- 语义:**"这是某种类型"**
- 设计目标:类型别名、组合、变换
- 特点:
- ✅ 联合 `|`
- ✅ 交叉 `&`
- ✅ 条件类型 `T extends U ? A : B`
- ✅ 映射类型
```ts
type ID = number | string;
type UserOrError = User | Error;
```
> 读起来像:"ID 可以是 number 或 string"
---
## 四、坚决用 `interface` 的场景
### 4.1 定义对象的结构契约
```ts
// ✅ 强烈推荐
interface CreateUserDto {
email: string;
password: string;
}
interface UserResponse {
id: number;
email: string;
}
```
好处:
- 一眼知道这是**一个对象**
- 后期加字段成本低
- 非常适合 DTO / VO / Entity
---
### 4.2 扩展第三方库(声明合并)
```ts
// ✅ 扩展 Express 的 Request
declare module 'express-serve-static-core' {
interface Request {
userId: number;
}
}
```
> **只有 `interface` 能做这件事,`type` 不行。**
---
### 4.3 React Props(99% 情况)
```ts
interface ButtonProps {
type?: 'primary' | 'default';
loading?: boolean;
onClick?: () => void;
}
```
原因:
- Props 本质是对象
- 组件消费方更容易理解
- 社区 & ESLint 生态默认偏好
---
## 五、坚决用 `type` 的场景
### 5.1 联合类型 / 可辨识联合
```ts
// ✅ 必须用 type
type ApiResult<T> =
| { ok: true; data: T }
| { ok: false; error: string };
```
> `interface` **无法直接表达"或"关系**。
---
### 5.2 字面量联合(替代 enum)
```ts
// ✅ 推荐
type Status = 'idle' | 'loading' | 'success' | 'error';
```
比 `enum` 更好:
- 无 JS 运行时成本
- Tree-shaking 友好
- 类型推导更强
---
### 5.3 函数类型
```ts
// ✅ 更清晰
type Handler = (req: Request, res: Response) => void;
// ❌ 可读性差
interface Handler {
(req: Request, res: Response): void;
}
```
---
### 5.4 工具类型 / 条件类型 / 映射类型
```ts
type ReadonlyUser = Readonly<User>;
type Nullable<T> = T | null;
type Maybe<T> = T | null | undefined;
type KeysOfType<T, U> = {
[K in keyof T]: T[K] extends U ? K : never;
}[keyof T];
```
> **`interface` 完全做不到这些。**
---
## 六、联合类型与 `interface` 的关系
### 6.1 `interface` 成员可以是联合类型
```ts
interface User {
id: number;
role: 'admin' | 'user' | 'guest'; // ✅ 成员是联合类型
status: 'active' | 'banned' | null;
}
```
### 6.2 多个 `interface` 可以组成联合类型
```ts
interface Admin {
type: 'admin';
permissions: string[];
}
interface User {
type: 'user';
email: string;
}
// ✅ 可辨识联合
type Account = Admin | User;
```
使用时必须**收窄类型**:
```ts
function handle(account: Account) {
if (account.type === 'admin') {
console.log(account.permissions); // ✅ Admin
} else {
console.log(account.email); // ✅ User
}
}
```
### 6.3 ❌ 不能这样写
```ts
// ❌ 非法语法
interface A | B {
x: number;
}
```
---
## 七、常见误区
### ❌ 误区一:混用导致团队混乱
```ts
interface User {
id: number;
}
// ❌ 风格不统一
type UserWithEmail = User & { email: string };
```
✅ 更推荐统一写法:
```ts
// 方案 A:全 interface
interface User {
id: number;
}
interface UserWithEmail extends User {
email: string;
}
// 方案 B:全 type
type User = {
id: number;
};
type UserWithEmail = User & {
email: string;
};
```
> **原则:同一"层级"的类型,尽量统一 `interface` 或 `type`。**
### ❌ 误区二:用可选属性模拟联合
```ts
// ❌ 不推荐
interface User {
id: number;
email?: string;
phone?: string;
}
```
问题:
- TS 无法强制 "email / phone 至少存在一个"
- 运行时还得自己校验
✅ 正确做法:
```ts
type Contact =
| { kind: 'email'; email: string }
| { kind: 'phone'; phone: string };
```
---
## 八、生产项目团队约定(可直接抄)
### 8.1 前端 / React
```ts
// Props / State / Ref —— 用 interface
interface ButtonProps {}
interface ListState {}
interface InputRef {}
// 事件回调 —— 用 type
type ButtonClickHandler = (id: number) => void;
// 状态集合 —— 用 type
type RequestStatus = 'idle' | 'loading' | 'success' | 'error';
```
### 8.2 Node / API
```ts
// 请求 / 响应 —— 用 interface
interface CreateUserReq {
email: string;
password: string;
}
interface CreateUserRes {
id: number;
email: string;
}
// 业务结果 —— 用 type
type ServiceResult<T> =
| { ok: true; data: T }
| { ok: false; error: string };
```
### 8.3 公共工具类型 —— 用 type
```ts
type Maybe<T> = T | null | undefined;
type Await<T> = T extends Promise<infer U> ? U : T;
type DeepPartial<T> = { [K in keyof T]?: DeepPartial<T[K]> };
```
---
## 九、联合类型速览(附录)
### 什么是联合类型?
一个值可以是**多种类型中的一种**:`A | B`
```ts
let value: string | number;
value = 'hello'; // ✅
value = 123; // ✅
value = true; // ❌
```
### 核心规则
1. **赋值时**:只要符合其中任意一个类型就行
2. **使用时**:只能访问所有类型**共有的成员**
3. 必须通过**类型收窄**才能安全使用特定成员
```ts
function printId(id: string | number) {
if (typeof id === 'string') {
console.log(id.toUpperCase()); // ✅
} else {
console.log(id.toFixed(2)); // ✅
}
}
```
### 常见场景
```ts
// 字面量联合
type Status = 'success' | 'error' | 'loading';
// API 响应封装
type Result<T> =
| { ok: true; data: T }
| { ok: false; error: string };
// 可空值
type UserOrNull = User | null;
```
---
## 十、一句话终极总结
> **`interface` = 定义"是什么"**
> **`type` = 描述"是什么关系"**
- 定义对象形状 → `interface`
- 组合 / 变换 / 联合 → `type`
- 同一项目内保持风格一致,比选哪个更重要
---
*文档版本:1.0 | 适用 TypeScript 5.x | 更新日期:2026-08-07*
- THE END -
最后修改:2026年8月7日
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:https://www.puxiaoshuai.top/?p=274
