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 接口:
- 基本接口定义
- 可选属性和只读属性
- 函数类型接口
- 可索引类型
- 类实现接口
- 接口继承
- 接口合并
- 接口与类型别名的区别
练习
- 定义一个
Student接口,包含姓名、年龄、成绩(可选) - 定义一个函数类型接口,接受两个数字并返回布尔值
- 创建一个类实现多个接口
- 使用索引签名定义一个允许动态键值的对象类型