TypeScript: TypeScript 接口 (interface)

最后更新:2026-08-26

接口(interface)是 TypeScript 定义对象形状的核心方式——它描述"一个对象必须有哪些属性和方法",但不提供实现。

1. 接口的基本语法

(1) 定义接口

TYPESCRIPT
interface User {
  name: string;
  age: number;
  email: string;
}

(2) 使用接口

TYPESCRIPT
let user: User = {
  name: "Charlie",
  age: 20,
  email: "xiaoming@example.com"
};

(3) 接口作为函数参数

TYPESCRIPT
function greet(user: User): string {
  return `你好,${user.name}!你今年${user.age}岁。`;
}

console.log(greet({ name: "Diana", age: 22, email: "hong@example.com" }));

输出:

TEXT 📖 仅展示
你好,Diana!你今年22岁。

(4) 接口描述方法

接口不仅能描述属性,还能描述方法签名:

TYPESCRIPT
interface Animal {
  name: string;
  speak(): string;
  move(distance: number): void;
}

let dog: Animal = {
  name: "旺财",
  speak() { return "汪汪!"; },
  move(distance) { console.log(`${this.name} 移动了 ${distance} 米`); }
};

console.log(dog.speak());   // "汪汪!"
dog.move(10);               // "旺财 移动了 10 米"

2. 可选属性与只读属性

(1) 可选属性 ?

TYPESCRIPT
interface Config {
  host: string;
  port: number;
  debug?: boolean;     // 可选
  timeout?: number;    // 可选
}

// 可以省略可选属性
let config: Config = { host: "localhost", port: 3000 };
let config2: Config = { host: "localhost", port: 3000, debug: true };

(2) 只读属性 readonly

TYPESCRIPT
interface Point {
  readonly x: number;
  readonly y: number;
}

let point: Point = { x: 1, y: 2 };
// point.x = 10;  // ❌ 只读属性不可修改

(3) ReadonlyArray 配合接口

TYPESCRIPT
interface TodoList {
  readonly name: string;
  readonly items: readonly string[];   // items 数组本身和内容都只读
}

let todo: TodoList = {
  name: "今日任务",
  items: ["写代码", "测试", "部署"]
};

// todo.items.push("新任务");  // ❌ 只读数组不可修改
// todo.name = "明日任务";     // ❌ 只读属性不可修改

3. 接口继承(extends)

接口可以通过 extends 继承其他接口,实现类型的组合和复用:

(1) 单继承

TYPESCRIPT
interface Person {
  name: string;
  age: number;
}

interface Employee extends Person {
  employeeId: string;
  department: string;
}

let emp: Employee = {
  name: "Charlie",
  age: 28,
  employeeId: "E001",
  department: "工程部"
};

(2) 多继承

接口可以同时继承多个接口:

TYPESCRIPT
interface Serializable {
  serialize(): string;
}

interface Loggable {
  log(message: string): void;
}

interface Entity extends Serializable, Loggable {
  id: number;
}

let item: Entity = {
  id: 1,
  serialize() { return JSON.stringify({ id: this.id }); },
  log(message) { console.log(`[${this.id}] ${message}`); }
};

(3) 覆写属性

子接口可以覆写父接口的属性类型,但必须兼容:

TYPESCRIPT
interface Base {
  data: string | number;
}

interface Derived extends Base {
  data: string;   // ✅ 收窄——string 是 string | number 的子类型
}

▶ 示例:用继承构建层次化类型

TYPESCRIPT
// 基础接口
interface Shape {
  color: string;
}

// 扩展接口
interface Square extends Shape {
  sideLength: number;
}

interface Circle extends Shape {
  radius: number;
}

// 使用
let square: Square = { color: "红色", sideLength: 10 };
let circle: Circle = { color: "蓝色", radius: 5 };

function describeShape(shape: Shape): string {
  return `一个${shape.color}的图形`;
}

console.log(describeShape(square));  // "一个红色的图形"
console.log(describeShape(circle));  // "一个蓝色的图形"
▶ 试一试

输出:

TEXT 📖 仅展示
一个红色的图形
一个蓝色的图形

4. 声明合并

interface 的独特能力——同名的 interface 会自动合并属性:

(1) 基本合并

TYPESCRIPT
interface Window {
  title: string;
}

interface Window {
  count: number;
}

// 等价于:
// interface Window {
//   title: string;
//   count: number;
// }

let win: Window = { title: "主窗口", count: 3 };

(2) 合并的规则

TYPESCRIPT
interface Calculator {
  compute(a: number, b: number): number;
}

interface Calculator {
  compute(a: string, b: string): string;
}

// 合并后 compute 有两个重载
let calc: Calculator = {
  compute(a: any, b: any): any {
    return a + b;
  }
};

console.log(calc.compute(1, 2));        // 3
console.log(calc.compute("a", "b"));    // "ab"

(3) 实际用途:扩展第三方类型

TYPESCRIPT
// 给内置的 Window 接口添加自定义属性
interface Window {
  myCustomProperty: string;
}

// 现在可以安全使用
// window.myCustomProperty = "hello";  // ✅
💡 这就是为什么用 interface 而不是 type 扩展第三方库的类型—— type 不支持声明合并,interface 可以。


5. 索引签名与接口

接口中也可以使用索引签名:

TYPESCRIPT
interface StringMap {
  [key: string]: string;
}

let translations: StringMap = {
  hello: "你好",
  goodbye: "再见",
  thanks: "谢谢"
};

// 添加新键值对
translations["sorry"] = "对不起";

索引签名和已知属性共存时,已知属性的类型必须兼容:

TYPESCRIPT
interface Config {
  [key: string]: string | number;
  host: string;       // ✅ string 是 string | number 的子类型
  port: number;       // ✅ number 是 string | number 的子类型
  // debug: boolean;  // ❌ boolean 不是 string | number 的子类型
}

6. interface 与 type 别名的区别

interface 和 type 都能定义对象类型,但有以下区别:

特性 interface type
对象类型 ✅ 主要用途 ✅ 也可以
声明合并 ✅ 支持 ❌ 不支持
继承 extends & 交叉类型
联合类型 ❌ 不能直接定义 type A = B | C
基本类型别名 ❌ 不能 type ID = string
计算属性 ❌ 不支持 ✅ 支持
instanceof ✅ class implements ❌ 不能

(1) 什么时候用 interface

(2) 什么时候用 type

📌 建议: 对象类型优先用 interface,联合类型和高级类型操作用 type。两者不是互斥的,可以在同一项目中混合使用。


❓ 常见问题

Q interface 和 type 到底该用哪个?
A 简单规则——定义"对象形状"用 interface,定义"类型别名/联合类型"用 type。如果团队已有约定,跟随团队即可。两者 90% 的场景等价,不用过度纠结。
Q 接口可以继承 type 定义的类型吗?
A 可以。interface extends 可以继承任何对象类型的 type 别名。反过来,type 也可以用交叉类型 & 组合 interface。两者互操作无障碍。
Q 声明合并有什么风险?
A 声明合并是 interface 的特性也是潜在风险——如果两个同名 interface 有同名但类型不同的属性,会编译报错。实际开发中,声明合并主要用来扩展第三方库的类型定义(.d.ts),日常代码中应避免定义同名 interface。
Q 接口可以描述函数类型吗?
A 可以,但一般用 type 更自然。interface Fn { (a: string): number } 等价于 type Fn = (a: string) => number,后者更简洁直观。函数类型推荐用 type。

📖 小节

📝 作业

  1. 基础题(难度⭐):定义 Book 接口(title、author、pages、isbn?),创建两本书对象并输出。其中 isbn 是可选属性。
  2. 进阶题(难度⭐⭐):定义 Shape 基础接口(color、area 方法),然后定义 CircleRectangle 分别继承 Shape 并添加各自的属性。写一个函数接收 Shape 类型参数并调用 area 方法。
  3. 挑战题(难度⭐⭐⭐):用声明合并给内置的 Array<T> 接口添加一个 last(): T | undefined 方法。然后在实际数组上调用这个方法,思考为什么这需要配合模块声明。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏