「TypeScript系列」TypeScript 接口/接口继承

一、TypeScript 接口

在 TypeScript 中,接口(Interfaces)是一个非常重要的概念,它用于定义对象的形状,即对象应该有哪些属性和方法。但接口本身并不实现任何功能,它只是定义了契约,其他类型(如类、对象字面量等)必须遵循这个契约。

以下是 TypeScript 接口的一些基本用法:

1. 属性接口

定义一个只包含属性的接口:

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

let person: Person = {
    name: 'Alice',
    age: 25
};

在这个例子中,Person 接口定义了一个对象应该具有 name(类型为 string)和 age(类型为 number)两个属性。然后,我们创建了一个 Person 类型的变量 person,并给它分配了一个满足这个接口定义的对象。

2. 方法接口

接口也可以定义方法:

interface Greeter {
    greet(name: string): void;
}

class PersonGreeter implements Greeter {
    greet(name: string) {
        return 'Hello, ' + name;
    }
}

let greeter: Greeter = new PersonGreeter();
console.log(greeter.greet('Alice'));  // 输出 "Hello, Alice"

在这个例子中,Greeter 接口定义了一个方法 greet,该方法接受一个 string 类型的参数,并返回 void(即不返回任何值)。然后,我们创建了一个 PersonGreeter 类,该类实现了 Greeter 接口,并提供了 greet 方法的实现。

3. 可选属性和只读属性

接口中的属性可以是可选的,也可以是只读的:

interface OptionalPerson {
    name: string;
    age?: number;  // 可选属性
    readonly id: number;  // 只读属性
}

let optionalPerson: OptionalPerson = {
    name: 'Bob',
    id: 123
};

// optionalPerson.age = 26;  // 错误,因为 age 是可选的,但并未在此处赋值
// optionalPerson.id = 456;  // 错误,因为 id 是只读的

在这个例子中,OptionalPerson 接口定义了一个可选的 age 属性和一个只读的 id 属性。注意,只读属性在第一次赋值后就不能再被修改。

4. 索引签名

接口也可以定义索引签名,用于描述对象可以有哪些索引以及这些索引的类型:

interface StringDictionary {
    [key: string]: string;
}

let dict: StringDictionary = {
    firstName: 'Alice',
    lastName: 'Smith'
};

// dict.someNumber = 123;  // 错误,因为索引签名只允许 string 类型的键和 string 类型的值

在这个例子中,StringDictionary 接口定义了一个索引签名,表示该对象可以有任意数量的字符串类型的键和对应的字符串类型的值。

二、TypeScript 接口继承

在 TypeScript 中,接口(Interfaces)是支持单继承的,即一个接口可以继承自另一个接口。但是,TypeScript 不支持接口的多继承,即一个接口不能直接继承自多个接口。这是因为多重继承在面向对象编程中往往会导致复杂的继承层次和潜在的“菱形问题”(diamond problem)。

1. 单继承

在 TypeScript 中,你可以使用 extends 关键字来定义接口的继承关系。以下是一个单继承的示例:

interface Animal {
    name: string;
    eat(): void;
}

interface Dog extends Animal {
    bark(): void;
}

let myDog: Dog = {
    name: 'Buddy',
    eat() {
        console.log('Dog is eating');
    },
    bark() {
        console.log('Woof woof!');
    }
};

在这个例子中,Dog 接口继承了 Animal 接口,并添加了 bark 方法。任何实现 Dog 接口的对象都必须实现 nameeatbark 这三个属性和方法。

2. 多继承的替代方案

虽然 TypeScript 不支持接口的多继承,但你可以使用交叉类型(Intersection Types)来实现类似的效果。交叉类型是将多个类型合并为一个类型,你可以将它看作是多重继承的“模拟”。

以下是一个使用交叉类型实现类似多继承的示例:

interface Bird {
    fly(): void;
    layEggs(): void;
}

interface Mammal {
    giveBirth(): void;
    eat(): void;
}

// 使用交叉类型合并 Bird 和 Mammal
type Bat = Bird & Mammal;

let myBat: Bat = {
    fly() {
        console.log('Flying');
    },
    layEggs() {
        console.log('Laying eggs (even though bats don\'t really do this)');
    },
    giveBirth() {
        console.log('Giving birth to live young');
    },
    eat() {
        console.log('Eating insects');
    }
};

在这个例子中,我们定义了两个接口 BirdMammal,然后使用交叉类型 Bat = Bird & Mammal 创建了一个新类型 Bat,它同时包含了 BirdMammal 的方法。任何符合 Bat 类型的对象都必须实现 flylayEggsgiveBirtheat 这四个方法。

虽然交叉类型在语法上看起来类似于多重继承,但它们之间有一些重要的区别。交叉类型不会创建一个继承层次结构,而是简单地将多个类型合并为一个类型。因此,它们不会遇到多重继承可能导致的复杂性和问题。

三、TypeScript 接口与数组

接口可以定义包含数组的属性。这允许你指定数组中的元素类型以及数组自身的其他属性(尽管数组本身没有除 length 和方法之外的属性)。

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

interface UserList {
    users: User[]; // 定义一个User类型的数组
    count: number; // 可能表示用户的数量
}

const userList: UserList = {
    users: [
        { name: 'Alice', age: 25 },
        { name: 'Bob', age: 30 },
    ],
    count: 2
};

在这个例子中,UserList 接口有一个 users 属性,它是一个 User 类型的数组。同时,UserList 还定义了一个 count 属性,可能用于存储用户的数量。

四、TypeScript 接口与联合类型

联合类型(Union Types)允许一个变量是几种类型之一。然而,接口本身并不直接与联合类型关联,但你可以使用接口与联合类型结合来定义复杂的类型。

联合类型通常使用 | 符号来定义,表示一个值可以是几种类型中的任何一种。

interface Square {
    kind: "square";
    size: number;
}

interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}

// 使用联合类型来定义一个变量可以是 Square 或 Rectangle
let shape: Square | Rectangle;

shape = { kind: "square", size: 100 };
// 或者
shape = { kind: "rectangle", width: 100, height: 50 };

// 使用类型守卫来区分不同的类型
function getArea(shape: Square | Rectangle): number {
    if (shape.kind === "square") {
        return shape.size * shape.size;
    } else {
        return shape.width * shape.height;
    }
}

在这个例子中,我们定义了两个接口 SquareRectangle,它们分别描述了正方形和矩形的属性。然后,我们定义了一个变量 shape,它可以是 SquareRectangle 类型之一。我们还定义了一个 getArea 函数,它接受一个 Square | Rectangle 类型的参数,并使用类型守卫(Type Guards)来区分不同的类型,并返回相应的面积。

需要注意的是,尽管在这个例子中我们没有直接在接口中使用联合类型,但联合类型与接口的结合使得我们可以定义更加复杂和灵活的类型系统。

五、相关链接

  1. TypeScript中文网
  2. TypeScript下载
  3. TypeScript文档
  4. 「TypeScript系列」TypeScript 简介及基础语法
  5. 「TypeScript系列」TypeScript 基础类型
  6. 「TypeScript系列」TypeScript 变量声明
  7. 「TypeScript系列」TypeScript 运算符
  8. 「TypeScript系列」TypeScript 条件语句
  9. 「TypeScript系列」TypeScript 循环
  10. 「TypeScript系列」TypeScript 函数
  11. 「TypeScript系列」TypeScript Number
  12. 「TypeScript系列」TypeScript String
  13. 「TypeScript系列」TypeScript Array(数组)
  14. 「TypeScript系列」TypeScript Map 对象
  15. 「TypeScript系列」TypeScript 元组
  16. 「TypeScript系列」TypeScript 联合类型/联合类型数组
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

·零落·

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值