讲解
interface(接口)是描述对象形状的另一种语法,能力上与 type 别名的对象写法高度重叠:都能定义属性、可选、只读、方法,都能被类 implements。社区共识的取舍原则是:描述对象/类的契约优先用 interface,其他(联合类型、函数类型、元组、映射类型等)只能用 type。
interface 有两个 type 不具备的特质。一是声明合并(declaration merging):同一作用域里写两个同名 interface,它们的成员会合并成一个。这个特性主要用于给第三方库的全局类型打补丁(比如扩展 Window 接口),日常业务代码很少主动用,但读到 .d.ts 文件时要看得懂。二是 extends 继承:interface Admin extends User 可以复用并扩展已有接口,语义直白;type 实现同样效果要用交叉类型 &,写法 type Admin = User & { role: string }。
性能层面有个细节:interface 的继承关系在编译器里是命名引用,报错信息更短、大型项目的类型检查略快;交叉类型在复杂组合下报错信息可能非常长。这也是「对象契约优先 interface」的一个现实理由。
示例
定义接口并被对象使用:
interface Article {
readonly id: number;
title: string;
tags: string[];
summary?: string;
}
const post: Article = {
id: 101,
title: "接口入门",
tags: ["typescript", "基础"],
};
console.log(`#${post.id} ${post.title} [${post.tags.join(", ")}]`);
extends 继承复用契约:
interface Person {
name: string;
age: number;
}
interface Employee extends Person {
employeeId: string;
department: string;
}
const emp: Employee = {
name: "小陈",
age: 28,
employeeId: "E-1024",
department: "研发",
};
console.log(`${emp.department} 的 ${emp.name}(工号 ${emp.employeeId})`);
类通过 implements 承诺遵守接口(implements 可以多个,用逗号分隔):
interface Printable {
print(): string;
}
interface Saveable {
save(): void;
}
class Report implements Printable, Saveable {
constructor(private title: string) {}
print(): string {
return `[报表] ${this.title}`;
}
save(): void {
console.log(`已保存:${this.title}`);
}
}
const r = new Report("月度总结");
console.log(r.print());
r.save();
常见坑
- interface 和 type 混用无标准:团队内统一约定(对象契约用 interface,其余用 type),比各自发挥重要。
- 想用 interface 表达联合类型:interface 只能描述对象形状,"a" | "b" 这种联合必须用 type。
- 意外的声明合并:同名 interface 悄悄合并会引入「我明明没写这个属性却报错」的困惑;命名尽量具体,避免与库的全局接口撞名。
- implements 只是编译期检查:接口在运行时不存在,instanceof 接口是不合法的——需要运行时判别时用类或判别字段(见类型收窄一章)。
小结
interface 与 type 对象写法能力重叠,约定:对象契约用 interface,其余用 type;interface 独有声明合并和 extends 继承;implements 让类承诺遵守契约。下一章讲函数类型。