目录

第五章:接口

本章概述

接口(Interface)是 TypeScript 最核心的特性之一,它定义了对象的“形状”,提供了强大的类型约束能力。本章将深入讲解接口的定义、使用和各种高级特性。

5.1 接口基础

5.1.1 什么是接口

接口是对对象结构的描述,它定义了对象应该包含哪些属性和方法。

```typescript 基本接口定义 interface Person { name: string; age: number; } 使用接口 const alice: Person = {

name: "Alice",
age: 25

};

类型检查 const bob: Person = { name: “Bob”, age: 30 }; ``` ==== 5.1.2 接口与类型别名的区别 ==== ```typescript 接口 interface Point {

x: number;
y: number;

}

类型别名 type Point2 = { x: number; y: number; }; 关键区别1:接口可以声明合并 interface Point {

z?: number;  // 扩展 Point 接口

}

关键区别2:接口更适合面向对象编程 interface Animal { name: string; } interface Dog extends Animal { breed: string; } 关键区别3:类型别名可以表示联合类型 type Status = “loading” | “success” | “error”; ```

5.2 接口属性

5.2.1 必需属性

```typescript interface User {

id: number;
name: string;
email: string;

}

const user: User = {

id: 1,
name: "Alice",
email: "alice@example.com"

};

缺少属性会报错 const incomplete: User = { id: 1, name: “Alice” Error: Property 'email' is missing }; ``` ==== 5.2.2 可选属性(Optional Properties) ==== 使用 ? 标记可选属性: ```typescript interface User { id: number; name: string; email?: string; 可选

phone?: string;      // 可选
address?: {
  city: string;
  country: string;
};

}

可以省略可选属性 const user1: User = { id: 1, name: “Alice” }; const user2: User = { id: 2, name: “Bob”, email: “bob@example.com” }; ``` ==== 5.2.3 只读属性(Readonly Properties) ==== 使用 readonly 标记只读属性: ```typescript interface Config { readonly apiUrl: string; readonly version: string; timeout: number; 可修改 }

const config: Config = {

apiUrl: "https://api.example.com",
version: "1.0.0",
timeout: 5000

};

config.timeout = 10000; OK config.apiUrl = “…”; Error: Cannot assign to 'apiUrl' 只读数组 interface TodoList {

readonly items: readonly string[];

}

const list: TodoList = {

items: ["Buy milk", "Walk dog"]

};

list.items.push(“New item”); Error list.items[0] = “Changed”; Error ```

5.3 函数类型

5.3.1 接口定义函数类型

```typescript 定义函数接口 interface SearchFunc { (source: string, subString: string): boolean; } 实现 const mySearch: SearchFunc = function(source, subString) {

return source.search(subString) > -1;

};

使用 console.log(mySearch(“hello world”, “world”)); true ```

5.3.2 带属性的函数

```typescript interface Counter {

(start: number): string;  // 调用签名
interval: number;         // 属性
reset(): void;            // 方法

}

function createCounter(): Counter {

const counter = function(start: number): string {
  return `Started at ${start}`;
} as Counter;

counter.interval = 1000;
counter.reset = function() {
  console.log("Counter reset");
};

return counter;

}

const c = createCounter(); console.log(c(10)); “Started at 10” console.log(c.interval); 1000 c.reset(); ```

5.3.3 构造函数签名

```typescript interface ClockConstructor {

new (hour: number, minute: number): ClockInterface;

}

interface ClockInterface {

tick(): void;

}

class DigitalClock implements ClockInterface {

constructor(h: number, m: number) {}
tick() {
  console.log("beep beep");
}

}

class AnalogClock implements ClockInterface {

constructor(h: number, m: number) {}
tick() {
  console.log("tick tock");
}

}

function createClock(

ctor: ClockConstructor,
hour: number,
minute: number

): ClockInterface {

return new ctor(hour, minute);

}

const digital = createClock(DigitalClock, 12, 17); const analog = createClock(AnalogClock, 7, 32); ```

5.4 索引签名(Index Signatures)

5.4.1 字符串索引签名

```typescript 定义字符串索引签名 interface StringDictionary { [key: string]: string; } const dict: StringDictionary = { name: “Alice”, country: “China” }; 使用 console.log(dict[“name”]); dict[“city”] = “Beijing”; ```

5.4.2 数字索引签名

```typescript interface NumberArray {

[index: number]: string;

}

const arr: NumberArray = [“a”, “b”, “c”]; console.log(arr[0]); “a” ``` ==== 5.4.3 混合索引签名 ==== ```typescript interface HybridDictionary { [key: string]: string | number; [index: number]: string; 数字索引的返回值必须是字符串索引的子类型

// 预定义属性
name: string;  // 必须兼容索引签名

}

const hybrid: HybridDictionary = {

name: "test",
0: "zero",
1: "one",
other: "value"

}; ```

5.4.4 只读索引签名

```typescript interface ReadonlyDictionary {

readonly [key: string]: number;

}

const readonlyDict: ReadonlyDictionary = {

x: 10,
y: 20

};

readonlyDict[“x”] = 100; Error ```

5.5 接口继承

5.5.1 单继承

```typescript interface Animal {

name: string;

}

interface Dog extends Animal {

breed: string;

}

const myDog: Dog = {

name: "Buddy",
breed: "Golden Retriever"

}; ```

5.5.2 多继承

```typescript interface Shape {

color: string;

}

interface PenStroke {

penWidth: number;

}

interface Square extends Shape, PenStroke {

sideLength: number;

}

const square: Square = {

color: "blue",
penWidth: 5,
sideLength: 10

}; ```

5.5.3 继承时的类型兼容性

```typescript interface Animal {

name: string;

}

interface Dog extends Animal {

breed: string;
bark(): void;

}

子类型可以赋值给父类型 const dog: Dog = { name: “Buddy”, breed: “Labrador”, bark() { console.log(“Woof!”); } }; const animal: Animal = dog; OK

父类型不能赋值给子类型 const wrongDog: Dog = { name: “Wrong” }; Error ``` ===== 5.6 接口与类 ===== ==== 5.6.1 类实现接口 ==== ```typescript interface ClockInterface { currentTime: Date; setTime(d: Date): void; } class Clock implements ClockInterface { currentTime: Date = new Date(); setTime(d: Date): void { this.currentTime = d; } constructor(h: number, m: number) {} } ``` ==== 5.6.2 多个接口 ==== ```typescript interface Printable { print(): void; } interface Loggable { log(): void; } class DocumentClass implements Printable, Loggable { print(): void { console.log(“Printing document…”); } log(): void { console.log(“Logging document activity…”); } } ``` ===== 5.7 接口的高级特性 ===== ==== 5.7.1 可选方法的接口 ==== ```typescript interface Plugin { name: string; init(): void; destroy?(): void; 可选方法

update?(config: object): void;  // 可选方法

}

const simplePlugin: Plugin = {

name: "Simple",
init() {
  console.log("Initialized");
}
// destroy 和 update 是可选的

};

const advancedPlugin: Plugin = {

name: "Advanced",
init() {
  console.log("Initialized");
},
destroy() {
  console.log("Destroyed");
},
update(config) {
  console.log("Updated", config);
}

}; ```

5.7.2 接口的声明合并

```typescript interface Window {

title: string;

}

interface Window {

ts: TypeScriptAPI;

}

结果等同于: interface Window { title: string; ts: TypeScriptAPI; } interface TypeScriptAPI { version: string; } const src: Window = { title: “TypeScript”, ts: { version: “5.0” } }; ``` ==== 5.7.3 严格的属性检查 ==== ```typescript interface SquareConfig { color?: string; width?: number; } function createSquare(config: SquareConfig): { color: string; area: number } { return { color: config.color || “red”, area: config.width ? config.width * config.width : 20 }; } 正常的对象字面量会经过额外属性检查 const mySquare = createSquare({ colour: “red”, width: 100 }); Error: 'colour' not in 'SquareConfig'

解决方法1:使用类型断言 const mySquare = createSquare({ colour: “red”, width: 100 } as SquareConfig); 解决方法2:添加字符串索引签名 interface SquareConfig2 {

color?: string;
width?: number;
[propName: string]: any;

}

解决方法3:将对象赋值给变量 const squareOptions = { colour: “red”, width: 100 }; const mySquare2 = createSquare(squareOptions); ``` ===== 5.8 接口的实际应用 ===== ==== 5.8.1 API 响应类型 ==== ```typescript 基础响应接口 interface ApiResponse<T> {

code: number;
message: string;
data: T;
timestamp: number;

}

用户相关 interface User { id: number; name: string; email: string; avatar?: string; } interface UserResponse extends ApiResponse<User> {} interface UsersResponse extends ApiResponse<User[]> {} 使用 async function fetchUser(id: number): Promise<UserResponse> {

const response = await fetch(`/api/users/${id}`);
return response.json();

}

类型安全的响应处理 const userResponse: UserResponse = await fetchUser(1); console.log(userResponse.data.name); TypeScript 知道 data 是 User ```

5.8.2 组件 Props 接口

```typescript React/Vue 组件 Props 定义 interface ButtonProps { 必需属性

label: string;
onClick: () => void;

// 可选属性
variant?: "primary" | "secondary" | "danger";
size?: "small" | "medium" | "large";
disabled?: boolean;
loading?: boolean;

// 样式
className?: string;
style?: React.CSSProperties;

// HTML 属性扩展
type?: "button" | "submit" | "reset";

}

使用 function Button(props: ButtonProps) { const { label, onClick, variant = “primary”, size = “medium”, disabled = false, loading = false } = props; return ( <button onClick={onClick} disabled={disabled || loading} className={`btn btn-${variant} btn-${size}`} > {loading ? “Loading…” : label} </button> ); } ``` ===== 5.9 本章小结 ===== 本章我们学习了: 1. 接口基础 - 定义对象结构,与类型别名的区别 2. 属性类型 - 必需属性、可选属性、只读属性 3. 函数类型 - 函数签名、带属性的函数、构造函数 4. 索引签名 - 字符串和数字索引签名 5. 接口继承 - 单继承和多继承 6. 类实现接口 - implements 关键字 7. 高级特性 - 可选方法、声明合并、严格属性检查 ===== 5.10 练习题 ===== ==== 练习 1:接口设计 ==== 设计一个完整的电商系统类型接口: - Product(商品):id, name, price, category, description?, imageUrl?, inStock - CartItem(购物车项):product, quantity - ShoppingCart(购物车):items, totalPrice, addItem(), removeItem(), clear() - Order(订单):id, items, totalAmount, status, createdAt, shippingAddress <details> <summary>参考答案</summary> ```typescript 基础类型 interface Product {

readonly id: string;
name: string;
price: number;
category: string;
description?: string;
imageUrl?: string;
inStock: boolean;

}

购物车项 interface CartItem { readonly product: Product; quantity: number; } 购物车 interface ShoppingCart {

readonly items: readonly CartItem[];
readonly totalPrice: number;
addItem(product: Product, quantity: number): void;
removeItem(productId: string): void;
updateQuantity(productId: string, quantity: number): void;
clear(): void;

}

订单状态 type OrderStatus = “pending” | “confirmed” | “shipped” | “delivered” | “cancelled”; 地址 interface Address {

street: string;
city: string;
state: string;
zipCode: string;
country: string;

}

订单 interface Order { readonly id: string; readonly items: readonly CartItem[]; readonly totalAmount: number; status: OrderStatus; readonly createdAt: Date; shippingAddress: Address; trackingNumber?: string; } API 响应 interface ApiResponse<T> {

success: boolean;
data?: T;
error?: string;

}

type ProductResponse = ApiResponse<Product>; type ProductsResponse = ApiResponse<Product[]>; type OrderResponse = ApiResponse<Order>; ```

</details>

练习 2:函数接口

实现一个事件系统接口:

```typescript 要求: 1. 定义 EventEmitter 接口 2. 支持 on(event, listener) 订阅事件 3. 支持 off(event, listener) 取消订阅 4. 支持 emit(event, …args) 触发事件 5. 支持 once(event, listener) 一次性订阅 ```

<details> <summary>参考答案</summary>

```typescript 事件监听器类型 type EventListener<T extends any[] = any[]> = (…args: T) ⇒ void; 事件发射器接口 interface EventEmitter {

on<T extends any[]>(event: string, listener: EventListener<T>): void;
off<T extends any[]>(event: string, listener: EventListener<T>): void;
emit<T extends any[]>(event: string, ...args: T): void;
once<T extends any[]>(event: string, listener: EventListener<T>): void;

}

实现 class SimpleEventEmitter implements EventEmitter { private listeners: Map<string, Set<EventListener» = new Map(); private onceListeners: Map<string, Set<EventListener» = new Map(); on<T extends any[]>(event: string, listener: EventListener<T>): void { if (!this.listeners.has(event)) { this.listeners.set(event, new Set()); } this.listeners.get(event)!.add(listener as EventListener); } off<T extends any[]>(event: string, listener: EventListener<T>): void { this.listeners.get(event)?.delete(listener as EventListener); this.onceListeners.get(event)?.delete(listener as EventListener); } emit<T extends any[]>(event: string, …args: T): void { const eventListeners = this.listeners.get(event); const onceEventListeners = this.onceListeners.get(event); eventListeners?.forEach(listener ⇒ listener(…args)); onceEventListeners?.forEach(listener ⇒ { listener(…args); this.onceListeners.get(event)?.delete(listener); }); } once<T extends any[]>(event: string, listener: EventListener<T>): void { if (!this.onceListeners.has(event)) { this.onceListeners.set(event, new Set()); } this.onceListeners.get(event)!.add(listener as EventListener); } } 使用 type UserEvents = {

login: [userId: string];
logout: [];
message: [content: string, sender: string];

};

const emitter = new SimpleEventEmitter();

emitter.on(“login”, (userId) ⇒ {

console.log(`User ${userId} logged in`);

});

emitter.once(“logout”, () ⇒ {

console.log("User logged out (once)");

});

emitter.emit(“login”, “user-123”); emitter.emit(“logout”); emitter.emit(“logout”); 不再触发 ``` </details> ==== 练习 3:索引签名应用 ==== 实现一个类型安全的配置管理器: ```typescript 要求: 1. 定义 ConfigManager 接口,支持任意字符串键 2. 类型安全:根据键推断值的类型 3. 支持 get(key), set(key, value), has(key) 方法 4. 使用泛型实现类型安全 ```

<details> <summary>参考答案</summary>

```typescript 配置项类型映射 interface ConfigSchema { “api.url”: string; “api.timeout”: number; “app.debug”: boolean; “app.name”: string; “cache.enabled”: boolean; “cache.ttl”: number; } type ConfigKey = keyof ConfigSchema; 配置管理器接口 interface ConfigManager {

get<K extends ConfigKey>(key: K): ConfigSchema[K];
set<K extends ConfigKey>(key: K, value: ConfigSchema[K]): void;
has(key: ConfigKey): boolean;
getAll(): Readonly<Partial<ConfigSchema>>;

}

实现 class DefaultConfigManager implements ConfigManager { private config: Partial<ConfigSchema> = {}; get<K extends ConfigKey>(key: K): ConfigSchema[K] { if (!(key in this.config)) { throw new Error(`Config key '${key}' not found`); } return this.config[key]!; } set<K extends ConfigKey>(key: K, value: ConfigSchema[K]): void { this.config[key] = value; } has(key: ConfigKey): boolean { return key in this.config; } getAll(): Readonly<Partial<ConfigSchema» { return { …this.config }; } } 使用 const config = new DefaultConfigManager();

config.set(“api.url”, “https://api.example.com”); config.set(“api.timeout”, 5000); config.set(“app.debug”, true);

const url = config.get(“api.url”); 类型: string const timeout = config.get(“api.timeout”); 类型: number

console.log(config.has(“api.url”)); true ``` </details> ==== 练习 4:接口继承实践 ==== 设计一个图形编辑器类型系统: ```typescript 要求: 1. 定义基础 Shape 接口,包含 x, y, id, render() 2. 定义 Colorful 接口,包含 fillColor, strokeColor 3. 定义 Movable 接口,包含 move(dx, dy), rotate(angle) 4. 创建 Circle 接口(继承 Shape, Colorful) 5. 创建 Rectangle 接口(继承 Shape, Colorful, Movable) 6. 创建 Group 接口,包含 shapes 数组 ```

<details> <summary>参考答案</summary>

```typescript 基础接口 interface Shape { readonly id: string; x: number; y: number; render(): void; getBounds(): { width: number; height: number }; } interface Colorful { fillColor: string; strokeColor: string; opacity?: number; } interface Movable { move(dx: number, dy: number): void; rotate(angle: number, centerX?: number, centerY?: number): void; scale(factor: number): void; } 圆形 interface Circle extends Shape, Colorful {

radius: number;

}

矩形 interface Rectangle extends Shape, Colorful, Movable { width: number; height: number; rotation?: number; } 多边形 interface Polygon extends Shape, Colorful, Movable {

points: Array<{ x: number; y: number }>;

}

组 interface Group extends Shape { readonly shapes: readonly Shape[]; addShape(shape: Shape): void; removeShape(id: string): void; getShape(id: string): Shape | undefined; } 实现示例 class CanvasCircle implements Circle {

readonly id: string;
x: number;
y: number;
radius: number;
fillColor: string;
strokeColor: string;
opacity: number = 1;
constructor(config: Omit<Circle, "render" | "getBounds">) {
  this.id = config.id;
  this.x = config.x;
  this.y = config.y;
  this.radius = config.radius;
  this.fillColor = config.fillColor;
  this.strokeColor = config.strokeColor;
  if (config.opacity !== undefined) {
    this.opacity = config.opacity;
  }
}
render(): void {
  console.log(`Rendering circle at (${this.x}, ${this.y}) with radius ${this.radius}`);
}
getBounds(): { width: number; height: number } {
  return {
    width: this.radius * 2,
    height: this.radius * 2
  };
}

}

使用 const circle: Circle = new CanvasCircle({ id: “circle-1”, x: 100, y: 100, radius: 50, fillColor: “red”, strokeColor: “black” }); circle.render(); console.log(circle.getBounds()); ``` </details> ===== 扩展阅读 ===== - 下一章:类 - TypeScript 官方文档 - 对象类型 - TypeScript 官方文档 - 接口