TypeScript 接口深入解析:从基础到高级(十三)

在 TypeScript 中,接口(Interface)是一种强大的工具,用于定义对象的结构。本文将详细介绍 TypeScript 接口的基本概念、如何使用接口、联合类型、数组接口以及接口继承等内容,并通过实际示例帮助你更好地理解和应用这些知识。

什么是接口?

接口是一种定义对象结构的方式,它描述了对象应该具有的属性和方法。接口在编译时被检查,确保对象符合预期的结构,但在运行时会被删除,不会影响最终生成的 JavaScript 代码。接口使得代码更加健壮和易于维护。

基本接口

假设我们需要定义一个表示人的接口,该接口包含 firstName 和 lastName 属性,以及一个 sayHi 方法。我们可以这样定义:

interface IPerson {
  firstName: string;
  lastName: string;
  sayHi: () => string;
}

接下来,我们可以创建一个符合这个接口的对象:

const customer: IPerson = {
  firstName: "Tom",
  lastName: "Hanks",
  sayHi: (): string => {
    return "Hi there";
  }
};

console.log("Customer 对象");
console.log(customer.firstName);
console.log(customer.lastName);
console.log(customer.sayHi());

同样地,我们可以创建另一个符合 IPerson 接口的对象:

const employee: IPerson = {
  firstName: "Jim",
  lastName: "Blakes",
  sayHi: (): string => {
    return "Hello!!!";
  }
};

console.log("Employee 对象");
console.log(employee.firstName);
console.log(employee.lastName);
console.log(employee.sayHi());

编译后的 JavaScript 代码如下:

const customer = {
  firstName: "Tom",
  lastName: "Hanks",
  sayHi: function() {
    return "Hi there";
  }
};

console.log("Customer 对象");
console.log(customer.firstName);
console.log(customer.lastName);
console.log(customer.sayHi());

const employee = {
  firstName: "Jim",
  lastName: "Blakes",
  sayHi: function() {
    return "Hello!!!";
  }
};

console.log("Employee 对象");
console.log(employee.firstName);
console.log(employee.lastName);
console.log(employee.sayHi());

输出结果为:

Customer 对象
Tom
Hanks
Hi there
Employee 对象
Jim
Blakes
Hello!!!

联合类型

在接口中,我们可以使用联合类型(Union Types)来定义属性可以接受多种类型的值。例如,假设我们有一个 RunOptions 接口,其中 commandline 属性可以是字符串、字符串数组或返回字符串的函数:

interface RunOptions {
  program: string;
  commandline: string | string[] | (() => string);
}

// commandline 是字符串
const options1: RunOptions = {
  program: "test1",
  commandline: "Hello"
};
console.log(options1.commandline);

// commandline 是字符串数组
const options2: RunOptions = {
  program: "test1",
  commandline: ["Hello", "World"]
};
console.log(options2.commandline[0]);
console.log(options2.commandline[1]);

// commandline 是一个函数表达式
const options3: RunOptions = {
  program: "test1",
  commandline: (): string => {
    return "**Hello World**";
  }
};
const fn: any = options3.commandline;
console.log(fn());

编译后的 JavaScript 代码如下:

const options1 = {
  program: "test1",
  commandline: "Hello"
};
console.log(options1.commandline);

const options2 = {
  program: "test1",
  commandline: ["Hello", "World"]
};
console.log(options2.commandline[0]);
console.log(options2.commandline[1]);

const options3 = {
  program: "test1",
  commandline: function() {
    return "**Hello World**";
  }
};
const fn = options3.commandline;
console.log(fn());

输出结果为:

Hello
Hello
World
**Hello World**

数组接口

在 TypeScript 中,我们还可以定义数组接口,指定数组的索引类型和元素类型。例如,我们可以定义一个字符串数组接口:

interface NameList {

  [index: number]: string;
}

const list: NameList = ["Google", "Runoob", "Taobao"];
console.log(list);

如果尝试将非字符串类型的元素添加到数组中,TypeScript 会报错:

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

// 错误:元素 1 不是 string 类型
// const list: NameList = ["Runoob", 1, "Taobao"];

编译时会显示以下错误:

test.ts:8:30 - error TS2322: Type 'number' is not assignable to type 'string'.

8 const list: NameList = ["Runoob", 1, "Taobao"];
                               ~

  test.ts:2:4
    2    [index: number]: string;
         ~~~~~~~~~~~~~~~~~~~~~~~~
    The expected type comes from this index signature.

Found 1 error.

字符串索引接口

除了数字索引,我们还可以定义字符串索引的接口。例如,假设我们有一个表示年龄的接口:

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

const ageList: Ages = {};
ageList["runoob"] = 15;
console.log(ageList);

如果尝试将非数字类型的值赋给索引,TypeScript 也会报错:

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

// 错误:Type '"google"' is not assignable to type 'number'.
// ageList[2] = "google";

接口继承

接口可以通过继承其他接口来扩展自己的功能。TypeScript 支持单继承和多继承。继承使用 extends 关键字。

单继承

假设我们有一个 Person 接口,表示一个人的基本信息,包括年龄。我们可以通过继承 Person 接口来定义一个 Musician 接口,表示音乐家的信息,包括喜欢的乐器:

interface Person {
  age: number;
}

interface Musician extends Person {
  instrument: string;
}

const drummer: Musician = {
  age: 27,
  instrument: "Drums"
};

console.log("年龄: " + drummer.age);
console.log("喜欢的乐器: " + drummer.instrument);

编译后的 JavaScript 代码如下:

const drummer = {
  age: 27,
  instrument: "Drums"
};

console.log("年龄: " + drummer.age);
console.log("喜欢的乐器: " + drummer.instrument);

输出结果为:

年龄: 27
喜欢的乐器: Drums

多继承

TypeScript 还支持多继承,即一个接口可以继承多个接口。例如,假设我们有两个接口 IParent1 和 IParent2,我们可以通过继承这两个接口来定义一个新的 Child 接口:

interface IParent1 {
  v1: number;
}

interface IParent2 {
  v2: number;
}

interface Child extends IParent1, IParent2 {}

const Iobj: Child = {
  v1: 12,
  v2: 23
};

console.log("value 1: " + Iobj.v1 + " value 2: " + Iobj.v2);

编译后的 JavaScript 代码如下:

const Iobj = {
  v1: 12,
  v2: 23
};

console.log("value 1: " + Iobj.v1 + " value 2: " + Iobj.v2);

输出结果为:

value 1: 12 value 2: 23

总结

通过本文,我们详细介绍了 TypeScript 接口的基本概念、如何使用接口、联合类型、数组接口以及接口继承等内容。接口是 TypeScript 中非常重要的特性,它可以帮助我们编写更加健壮和可维护的代码。希望本文能帮助你更好地理解和应用这些知识。