类与接口的关系
在TypeScript中,类和接口是构建复杂类型系统的核心工具。类提供具体的实现,而接口定义行为的契约,二者通过多种方式协作,共同实现代码的抽象与复用。
类的定义与实现
类是面向对象编程的基础,用于描述对象的属性和方法。TypeScript中的类支持继承、封装和多态等特性。例如:
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number = 0) {
console.log(`${this.name} moved ${distance}m.`);
}
}
接口的基本概念
接口纯粹是类型定义的工具,不包含具体实现。它用于描述对象应该具有的形状:
interface Movable {
move(distance: number): void;
}
类实现接口
类可以通过implements
关键字实现一个或多个接口,这强制类必须满足接口定义的契约:
class Car implements Movable {
move(distance: number) {
console.log(`Car moved ${distance}km.`);
}
}
接口继承类
TypeScript允许接口继承类的成员定义(不包括实现),这在需要复用类结构时非常有用:
class Point {
x: number;
y: number;
}
interface Point3D extends Point {
z: number;
}
const point: Point3D = { x: 1, y: 2, z: 3 };
类类型与接口的异同
特性 | 类 | 接口 |
---|---|---|
实现 | 包含具体实现 | 只有类型定义 |
运行时存在 | 是 | 否 |
可实例化 | 是 | 否 |
继承 | 单继承 | 多继承 |
接口扩展类时的特殊情况
当接口继承包含私有或受保护成员的类时,只有该类的子类才能实现这个接口:
class Control {
private state: any;
}
interface SelectableControl extends Control {
select(): void;
}
class Button extends Control implements SelectableControl {
select() {}
}
// 错误:缺少私有成员state
class TextBox implements SelectableControl {
select() {}
}
混合类型接口
接口可以描述函数、对象和索引签名的组合,这种灵活性在处理复杂场景时特别有用:
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
let counter = function(start: number) {} as Counter;
counter.interval = 123;
counter.reset = function() {};
return counter;
}
抽象类与接口的对比
抽象类介于普通类和接口之间,可以包含部分实现:
abstract class Department {
constructor(public name: string) {}
abstract printMeeting(): void;
}
class AccountingDepartment extends Department {
printMeeting() {
console.log('Accounting meeting');
}
}
接口合并的特性
同名的接口会自动合并,这在扩展第三方类型时很常见:
interface Box {
height: number;
}
interface Box {
width: number;
}
// 最终Box接口包含height和width
const box: Box = { height: 1, width: 2 };
类静态部分与实例部分的区别
类有静态部分和实例部分,接口只能约束实例部分:
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick(): void;
}
function createClock(
ctor: ClockConstructor,
hour: number,
minute: number
): ClockInterface {
return new ctor(hour, minute);
}
使用接口描述函数类型
接口可以描述函数签名,这在定义回调时特别有用:
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc = function(src, sub) {
return src.search(sub) > -1;
};
可索引类型接口
接口可以描述数组和字典等可索引类型:
interface StringArray {
[index: number]: string;
}
let myArray: StringArray = ['Bob', 'Fred'];
接口与类型别名的区别
虽然接口和类型别名有时可以互换,但它们有本质区别:
type Alias = { num: number }
interface Interface {
num: number;
}
// 类型别名不能extends或implements
class A implements Interface {
num = 1;
}
接口描述构造函数
通过特殊的构造签名,接口可以描述构造函数:
interface SomeConstructor {
new (s: string): SomeInterface;
}
interface SomeInterface {
doSomething(): void;
}
function createInstance(ctor: SomeConstructor, s: string): SomeInterface {
return new ctor(s);
}
接口的readonly属性
接口可以定义只读属性,增强类型安全性:
interface Point {
readonly x: number;
readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // 错误!
类实现多个接口
TypeScript支持一个类实现多个接口,这比多重继承更灵活:
interface Flyable {
fly(): void;
}
interface Swimmable {
swim(): void;
}
class Duck implements Flyable, Swimmable {
fly() { console.log('Flying'); }
swim() { console.log('Swimming'); }
}
接口中的可选属性
接口属性可以标记为可选,增加灵活性:
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig) {
// ...
}
接口描述复杂对象
接口可以描述包含多种类型的复杂对象结构:
interface NestedObject {
id: string;
metadata: {
createdAt: Date;
updatedAt?: Date;
tags: string[];
};
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn