TypeScript 中的接口(Interface)详解
接口(interface) 是 TypeScript 中最常用、最重要的类型定义方式之一,主要用于描述对象的形状(shape),定义对象应该具有哪些属性、方法及其类型。它是 TypeScript 类型系统的核心,帮助实现结构化类型检查(structural typing)。
1. 基本用法:定义对象形状
interface Person {
name: string;
age: number;
}
let user: Person = {
name: "Alice",
age: 30
};
// user = { name: "Bob" }; // 错误:缺少 age 属性
// user = { name: "Charlie", age: "25" }; // 错误:age 必须是 number
TypeScript 是结构化类型系统:只要对象满足接口要求的形状,即使没有显式声明 implements,也能赋值。
let admin = { name: "Eve", age: 28, role: "admin" }; // 多余属性 role
let p: Person = admin; // OK!TypeScript 允许额外属性(新鲜度检查例外情况见后文)
2. 可选属性(Optional Properties)
使用 ? 表示属性可选:
interface Book {
title: string;
author: string;
pages?: number; // 可选
readonly isbn: string; // 只读(见下文)
}
let novel: Book = {
title: "1984",
author: "Orwell",
isbn: "978-0141036144"
// pages 可省略
};
3. 只读属性(Readonly Properties)
使用 readonly 防止属性被修改:
interface Point {
readonly x: number;
readonly y: number;
}
let origin: Point = { x: 0, y: 0 };
// origin.x = 10; // 错误:只读
- 只读数组:
readonly string[]或ReadonlyArray<string>
let names: readonly string[] = ["Alice", "Bob"];
// names.push("Charlie"); // 错误
4. 函数类型(Function Types)
接口可以描述函数形状:
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc = function(src, sub) {
return src.includes(sub);
};
mySearch("hello", "ell"); // true
5. 索引签名(Index Signatures)
允许动态属性名:
interface StringDictionary {
[key: string]: string; // 键为 string,值为 string
length: number; // 固定属性必须兼容索引签名类型
}
let dict: StringDictionary = {
name: "Alice",
job: "Engineer",
length: 2 // OK,number 可赋值给 string(宽松规则)
};
- 也可以是
number索引:[index: number]: string
6. 接口继承(Extends)
接口可以继承一个或多个接口:
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
bark(): void;
}
let dog: Dog = {
name: "Buddy",
breed: "Golden Retriever",
bark() { console.log("Woof!"); }
};
多继承:
interface Flyable {
fly(): void;
}
interface Swimmable {
swim(): void;
}
interface Duck extends Animal, Flyable, Swimmable {
quack(): void;
}
7. 接口 vs 类型别名(type)
| 特性 | interface | type alias |
|---|---|---|
| 定义对象形状 | 推荐 | 可行 |
| 联合类型 / 字面量类型 | 不支持 | 支持 |
| 原始类型映射 | 不支持 | 支持(如 type Id = string | number) |
| 可重复声明(声明合并) | 支持(同名接口会合并) | 不支持 |
| 实现类(implements) | 支持 | 不支持(class 只能 implements interface) |
| 扩展方式 | extends | &(交叉类型) |
推荐:
- 定义对象/函数形状 → 用
interface - 需要联合、映射、条件类型 → 用
type
8. 声明合并(Declaration Merging)
同名接口会自动合并(非常有用,如扩展第三方库):
interface User {
name: string;
}
interface User {
age: number;
}
// 等价于:
interface User {
name: string;
age: number;
}
let u: User = { name: "Tom", age: 25 };
常用于扩展全局对象或第三方库类型。
9. 混合类型(Hybrid Types)
接口可以同时描述对象和函数(少见但强大):
interface Counter {
(start: number): string; // 作为函数
count: number; // 属性
reset(): void; // 方法
}
let counter: Counter = (() => {
let c = 0;
let fn = function(start: number) {
c = start;
return `Count: ${c}`;
};
fn.count = c;
fn.reset = () => { c = 0; };
return fn as Counter;
})();
10. 最佳实践建议
| 建议 | 说明 |
|---|---|
| 对象形状优先用 interface | 可读性强,支持声明合并 |
| 可选属性放后面 | 保持一致性 |
| 只读属性用于不可变数据 | 如 ID、配置 |
| 函数类型复杂时用 interface | 比 (params) => return 更清晰 |
| 扩展第三方库时用 interface | 利用声明合并 |
开启 "strict": true | 包括 strictPropertyInitialization 等 |
小结:接口速查表
| 特性 | 语法示例 |
|---|---|
| 基本接口 | interface Person { name: string; age: number; } |
| 可选属性 | pages?: number; |
| 只读属性 | readonly id: number; |
| 函数属性 | greet(name: string): void; |
| 索引签名 | [prop: string]: any; |
| 继承 | interface Admin extends Person { role: string; } |
| 函数类型接口 | interface Fn { (x: number): string; } |
接口是 TypeScript 中定义对象合约的最优雅方式,结合继承、声明合并等特性,能极大提升大型项目的类型安全和可维护性。
如果您想深入了解类实现接口(implements)、接口与抽象类的区别、工具类型(如 Partial、Pick)与接口结合,或者需要实际项目示例,请告诉我!