跳到主要内容

TypeScript 接口

接口(Interface)是 TypeScript 的核心特性之一,用于定义对象的形状、函数类型、类结构等。

基本接口

定义对象形状

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

// 使用接口
const user: User = {
name: "张三",
age: 25,
email: "[email protected]"
};

// 属性数量必须匹配
const invalidUser: User = {
name: "张三",
age: 25
// 错误:缺少 email 属性
};

解释:接口定义了对象必须具有的属性和类型。使用接口的对象必须完全匹配接口定义。

可选属性

interface Product {
id: number;
name: string;
price: number;
description?: string; // 可选属性
tags?: string[]; // 可选属性
}

const product1: Product = {
id: 1,
name: "TypeScript 入门",
price: 99.9
};

const product2: Product = {
id: 2,
name: "TypeScript 进阶",
price: 129.9,
description: "深入学习 TypeScript",
tags: ["编程", "TypeScript"]
};

解释:可选属性使用 ? 标记,对象可以选择是否包含该属性。

只读属性

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

const point: Point = {
x: 10,
y: 20
};

// point.x = 15; // 错误:只读属性不能修改

解释readonly 关键字表示属性只能在创建时赋值,之后不能修改。

只读数组

let numbers: ReadonlyArray<number> = [1, 2, 3, 4, 5];

// numbers[0] = 10; // 错误
// numbers.push(6); // 错误
// numbers.length = 0; // 错误

// 可以重新赋值
numbers = [10, 20, 30]; // OK

额外属性检查

精确匹配

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

// 直接赋值会检查额外属性
const user: User = {
name: "张三",
age: 25,
// gender: "male" // 错误:对象字面量只能指定已知属性
};

// 使用类型断言绕过检查
const user2: User = {
name: "张三",
age: 25,
gender: "male"
} as User; // OK,但不推荐

索引签名

interface FlexibleObject {
name: string;
age: number;
[key: string]: any; // 允许其他任意属性
}

const obj: FlexibleObject = {
name: "张三",
age: 25,
gender: "male",
address: "北京"
};

解释:索引签名允许对象具有额外的属性。

函数类型

定义函数类型

// 方式一:在接口中定义调用签名
interface SearchFunc {
(source: string, subString: string): boolean;
}

// 使用
const mySearch: SearchFunc = function(source, subString) {
return source.indexOf(subString) > -1;
};

// 方式二:使用类型别名
type Calculate = (a: number, b: number) => number;

const add: Calculate = (a, b) => a + b;
const multiply: Calculate = (a, b) => a * b;

解释:函数类型接口定义了函数的参数类型和返回值类型。

函数重载

// 重载签名
function getValue(value: number): number;
function getValue(value: string): string;
function getValue(value: boolean): boolean;

// 实现签名
function getValue(value: number | string | boolean): number | string | boolean {
return value;
}

const num = getValue(123); // number 类型
const str = getValue("hello"); // string 类型
const bool = getValue(true); // boolean 类型

解释:函数重载允许函数根据不同的参数类型返回不同的类型。

可索引类型

字符串索引

interface StringArray {
[index: number]: string;
}

let myArray: StringArray = ["张三", "李四", "王五"];
let first: string = myArray[0]; // "张三"

数字索引

interface NumberDictionary {
[index: string]: number;
}

let scores: NumberDictionary = {
"张三": 95,
"李四": 88,
"王五": 92
};

混合索引

interface MixedDictionary {
[index: string]: number | string;
length: number; // number 类型,与索引签名兼容
name: string; // string 类型,与索引签名兼容
// error: boolean; // 错误:boolean 不兼容 number | string
}

类类型

实现接口

interface ClockInterface {
currentTime: Date;
setTime(d: Date): void;
}

class Clock implements ClockInterface {
currentTime: Date = new Date();

setTime(d: Date) {
this.currentTime = d;
}

constructor(h: number, m: number) {}
}

解释:类使用 implements 关键字实现接口,必须实现接口定义的所有属性和方法。

多接口实现

interface Printable {
print(): void;
}

interface Loggable {
log(): void;
}

class Document implements Printable, Loggable {
print() {
console.log("打印文档");
}

log() {
console.log("记录日志");
}
}

类静态部分与实例部分

// 类的实例部分
interface ClockInterface {
tick(): void;
}

// 类的静态部分(构造函数)
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}

// 工厂函数
function createClock(
ctor: ClockConstructor,
hour: number,
minute: number
): ClockInterface {
return new ctor(hour, minute);
}

class DigitalClock implements ClockInterface {
constructor(h: number, m: number) {}

tick() {
console.log("beep beep");
}
}

const digital = createClock(DigitalClock, 12, 17);

解释:类有两个类型:静态部分的类型和实例部分的类型。接口只能描述实例部分,构造函数签名需要单独定义。

接口继承

单继承

interface Animal {
name: string;
}

interface Dog extends Animal {
breed: string;
}

const dog: Dog = {
name: "旺财",
breed: "金毛"
};

多继承

interface Printable {
print(): void;
}

interface Loggable {
log(): void;
}

interface Document extends Printable, Loggable {
content: string;
}

const doc: Document = {
content: "Hello",
print() {
console.log(this.content);
},
log() {
console.log("Logging:", this.content);
}
};

解释:接口可以多继承,合并多个接口的成员。

接口合并

同名接口会自动合并:

interface Box {
height: number;
width: number;
}

interface Box {
depth: number;
}

// 合并后的 Box
const box: Box = {
height: 10,
width: 20,
depth: 30
};

非函数成员

非函数成员必须唯一,否则类型必须相同:

interface Document {
title: string;
}

interface Document {
// title: number; // 错误:后续声明的同名属性类型必须相同
author: string;
}

函数成员

函数成员会被重载:

interface Alarm {
alert(): void;
}

interface Alarm {
alert(message: string): void;
}

// 合并后的重载
function alert(): void;
function alert(message: string): void;

接口 vs 类型别名

相同点

// 都可以定义对象类型
interface UserInterface {
name: string;
age: number;
}

type UserType = {
name: string;
age: number;
};

// 都可以定义函数类型
interface FuncInterface {
(x: number): number;
}

type FuncType = (x: number) => number;

不同点

// 1. 接口可以合并,类型别名不能
interface Box {
width: number;
}
interface Box {
height: number;
}
// Box 现在有 width 和 height

// 2. 类型别名可以表示联合类型、元组等
type ID = string | number;
type Point = [number, number];

// 3. 接口使用 extends,类型别名使用 &
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}

type Animal2 = {
name: string;
};
type Dog2 = Animal2 & {
breed: string;
};

// 4. 类型别名可以使用 typeof 获取类型
const user = { name: "张三", age: 25 };
type UserType = typeof user;

选择建议

  • 使用 interface 定义对象形状、类实现
  • 使用 type 定义联合类型、元组、映射类型等

小结

本章我们学习了 TypeScript 接口:

  1. 基本接口定义
  2. 可选属性和只读属性
  3. 函数类型接口
  4. 可索引类型
  5. 类实现接口
  6. 接口继承
  7. 接口合并
  8. 接口与类型别名的区别

练习

  1. 定义一个 Student 接口,包含姓名、年龄、成绩(可选)
  2. 定义一个函数类型接口,接受两个数字并返回布尔值
  3. 创建一个类实现多个接口
  4. 使用索引签名定义一个允许动态键值的对象类型