先给结论:TypeScript 里描述对象结构、需要被类实现或要留给人扩展时,用 interface;要表达联合类型、交叉类型、元组或条件类型时,只能用类型别名。两者在描述普通对象时几乎等价,差异集中在扩展方式上。

新手最常问"哪个更好",其实这是个伪问题。真正决定选择的是三个具体问题:要不要被类实现、要不要表达组合关系、以及这个类型会不会被别人补声明。
本文用同一份数据结构各写一遍,把差异逐条摆出来,并给出可直接照搬的选择标准。示例行为以当前主流版本为准,旧版本在错误信息上会有差异。
先看结论
- 要被类实现、要留给别人扩展:用 interface,扩展语法直观且支持重复声明合并。
- 要表达组合或运算:用类型别名,联合类型与交叉类型只有它能写。
- 描述普通对象时两者等价:团队有约定就按约定来,没有约定默认先用 interface。
一、最小示例:同一个结构写两遍
把问题定死:描述一个用户对象,包含编号、姓名和可选邮箱。
用 interface 写是这样:
interface User {
id: number;
name: string;
email?: string;
}
function printUser(u: User): void {
console.log(u.id, u.name);
}
这段做的是声明一个对象结构并把它用作参数类型。它还可以被类实现:
class AdminUser implements User {
id = 1;
name = 'admin';
}
这段做的是让类显式实现这个结构,缺少字段会在编译阶段报错。预期结果(未在本机执行):字段齐全时通过,缺一个字段就报缺失错误。
用类型别名写同一份结构:
type UserAlias = {
id: number;
name: string;
email?: string;
};
这段做的是用别名描述同样的结构。字段层面完全等价,区别发生在扩展时:要扩展得靠交叉类型,而 interface 用的是继承语法。日常写业务代码时,这个差别几乎感觉不到,只有当类型被多处引用、需要层层叠加时才会暴露出来。
二、三点比较
2.1 扩展方式不同
interface 用继承语法扩展,读起来和常见面向对象语言一致;别名则要用交叉类型把两个类型拼起来。两者结果相似,但后者在字段冲突时不会报错,而是把冲突字段变成不可满足的类型,排查起来更费劲。
2.2 表达能力不同
联合类型、条件类型、元组这些只有别名能写。想表达"要么是字符串要么是数字",interface 无能为力。
type ID = string | number;
这段做的是用别名表达联合类型。这是它最不可替代的场景,也是判断标准里最硬的一条。
2.3 重复声明的行为不同
同名 interface 会被自动合并,同名别名会直接报重复声明。给第三方库补字段时,合并是有用的;自己项目里则可能造成意外覆盖,尤其在多人协作、同名文件分散在不同目录时最难发现。

类型语法细节可以对照 TypeScript 教程 确认。
三、边界与取舍
3.1 错误信息可读性
interface 的名字会直接出现在错误提示里,别名在复杂运算后可能被展开成一长串。类型结构复杂时这一点很影响排错效率,也是很多团队默认选 interface 的真实原因。
3.2 索引签名与映射类型
需要基于已有类型批量生成新类型时,映射类型配合别名更顺手。interface 在这一块的表达力有限,写出来的代码往往更长。
3.3 性能差异可以忽略
两种写法在编译耗时上的差别极小,不该作为选择依据。真正的取舍点是可扩展性与表达能力,而不是谁更快。
3.4 与运行时校验的关系
无论选哪一种,类型都只在编译阶段存在,接口返回的数据并不会因此被自动校验。要做运行时校验得另写一套逻辑,这一点常被忽略。
语言基础可以对照 JavaScript 教程 里的对象章节补一补。
四、验证清单
4.1 先问会不会被类实现
会就用 interface,这是最直接的判断。
4.2 再问要不要组合
要写联合类型或条件类型,答案只能是类型别名。
4.3 统一团队约定
同一个项目里不要两种风格混用,写进约定里比每次讨论更高效。
4.4 验证清单
- 检查:确认同一个类型没有既用 interface 又用类型别名重复定义
- 运行:改一个字段,观察编译错误是否指向定义处
- 预期:错误提示清晰,能直接定位到字段
- 失败:错误被展开成一长串,说明类型运算过深
- 修复:给中间结果起名字,拆开复杂的类型运算
- 验证:加一个必填字段后全量编译,确认所有引用处都报错

总结
interface 与类型别名在描述对象时几乎等价,差异集中在三点:扩展方式、表达能力、重复声明的行为。把这三点过一遍,选择自然就出来了。
三条要点带走:
- 要被类实现或留给人扩展,优先 interface;
- 联合类型、交叉类型与条件类型只能用类型别名;
- 复杂运算会让错误信息变长,给中间结果起名字能救回来。
下一步建议在项目里写死一条约定:对象结构默认 interface,组合与运算一律用别名。语法细节可以对照 JavaScript 速查手册 再核一遍。
常见问题
Q:有了 interface 为什么还要别名?
A:因为联合类型、条件类型这些组合能力只有别名能表达。interface 描述的是结构,别名描述的是类型的运算结果。
Q:同名声明为什么有的能合并有的报错?
A:interface 支持声明合并,别名不允许重复声明。给第三方类型补字段时用前者,自己项目里要注意别误覆盖。
Q:用交叉类型扩展会有什么坑?
A:字段冲突时不会直接报错,而是变成不可满足的类型,等到使用时才暴露。用继承语法能在定义处就发现问题。
Q:类型报错被展开成一长串怎么办?
A:给中间结果起名字,把复杂的类型运算拆成几步。可读性会好很多,错误提示也会收敛。
Q:团队里两种都在用,需要统一吗?
A:建议统一。混用不会报错,但会增加阅读成本,也会让"什么时候该用哪个"变成反复讨论的问题。
延伸学习
想把这个知识点补扎实,可以按这个顺序来:
- 想把这块系统过一遍,TypeScript 入门到进阶课程 可以跟一遍;
- 读一篇相关笔记 TypeScript 接口数据校验,看别人踩过的坑;
- 想随手验证一段代码,用 JavaScript 在线运行 跑一遍最快。

TRAE-AI编程



