讲解

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 让类承诺遵守契约。下一章讲函数类型。