跳到主要内容

TypeScript 接口

摘要:在本教程中,你将了解 TypeScript 接口以及如何使用它们来强制进行类型检查。

TypeScript 接口简介

TypeScript 接口定义代码中的契约。 它们还为类型检查提供显式名称。

让我们从一个简单的例子开始:

function getFullName(person: {
firstName: string;
lastName: string
}) {
return `${person.firstName} ${person.lastName}`;
}

let person = {
firstName: 'John',
lastName: 'Doe'
};

console.log(getFullName(person));

输出:

John Doe

在此示例中,TypeScript 编译器检查传递给 getFullName() 函数的参数。

如果参数有两个类型为字符串的属性,则 TypeScript 编译器会通过检查。 否则,它会发出错误。

从代码中可以清楚地看到,函数参数的类型注释使得代码难以阅读。

为了解决这个问题,TypeScript 引入了接口的概念。

下面使用一个名为 Person 的接口,它有两个字符串属性:

interface Person {
firstName: string;
lastName: string;
}

按照约定,接口名称采用驼峰式大小写形式。 它们使用单个大写字母来分隔名称中的单词。 例如,PersonUserProfileFullName

定义 Person 接口后,可以将其用作类型。 并且可以用接口名称注释函数参数:

function getFullName(person: Person) {
return `${person.firstName} ${person.lastName}`;
}

let john = {
firstName: 'John',
lastName: 'Doe'
};

console.log(getFullName(john));

现在的代码比以前更容易阅读。

getFullName() 函数将接受具有两个字符串属性的任何参数。 而且它不必恰好有两个字符串属性。 请参见以下示例:

以下代码声明一个具有四个属性的对象:

let jane = {
firstName: 'Jane',
middleName: 'K.'
lastName: 'Doe',
age: 22
};

由于 jane 对象有两个字符串属性 firstNamelastName,因此可以将其传递到 getFullName() 函数,如下所示:

let fullName = getFullName(jane);
console.log(fullName); // Jane Doe

可选属性

接口可以具有可选属性。 要声明可选属性,请在声明中的属性名称末尾使用问号 (?),如下所示:

interface Person {
firstName: string;
middleName?: string;
lastName: string;
}

在此示例中,Person 接口有两个必需属性和一个可选属性。

下面展示了如何在 getFullName() 函数中使用 Person 接口:

function getFullName(person: Person) {
if (person.middleName) {
return `${person.firstName} ${person.middleName} ${person.lastName}`;
}
return `${person.firstName} ${person.lastName}`;
}

只读属性

如果属性仅在首次创建对象时才可修改,则可以在属性名称前使用 readonly 关键字:

interface Person {
readonly ssn: string;
firstName: string;
lastName: string;
}

let person: Person;
person = {
ssn: '171-28-0926',
firstName: 'John',
lastName: 'Doe'
}

在此示例中,ssn 属性无法更改:

person.ssn = '171-28-0000';

报错:

error TS2540: Cannot assign to 'ssn' because it is a read-only property.

函数类型

除了用于描述含有属性的对象之外,接口还允许你描述函数类型。

要描述函数类型,请将接口分配给包含带有类型和返回类型的参数列表的函数签名。 例如:

interface StringFormat {
(str: string, isUpper: boolean): string
}

现在,你可以使用这个函数类型接口了。

下面展示了如何声明函数类型的变量并为其分配相同类型的函数值:

let format: StringFormat;

format = function (str: string, isUpper: boolean) {
return isUpper ? str.toLocaleUpperCase() : str.toLocaleLowerCase();
};

console.log(format('hi', true));

输出:

HI

请注意,参数名称不需要与函数签名匹配。 以下示例与上面的示例等效:

let format: StringFormat;

format = function (src: string, upper: boolean) {
return upper ? src.toLocaleUpperCase() : src.toLocaleLowerCase();
};

console.log(format('hi', true));

StringFormat 接口确保实现它的函数的所有调用者都传递所需的参数:一个 string 和一个 boolean

即使把 lowerCase 赋值给没有第二个参数的函数,以下代码也可以正常工作:

let lowerCase: StringFormat;
lowerCase = function (str: string) {
return str.toLowerCase();
}

console.log(lowerCase('Hi', false));

请注意,调用 lowerCase() 函数时会传递第二个参数。

Class 类型

如果你使用过 Java 或 C#,你会发现接口的主要用途是在不相关的类之间定义契约。

例如,以下 Json 接口可以由任何不相关的类实现:

interface Json {
toJSON(): string
}

下面声明了一个实现 Json 接口的类:

class Person implements Json {
constructor(private firstName: string,
private lastName: string) {
}
toJson(): string {
return JSON.stringify(this);
}
}

Person 类中,我们实现了 Json 接口的 toJson() 方法。

以下示例展示了如何使用 Person 类:

let person = new Person('John', 'Doe');
console.log(person.toJson());

输出:

{"firstName":"John","lastName":"Doe"}

概括

  • TypeScript 接口在代码中定义契约并为类型检查提供显式名称。
  • 接口可能具有可选属性或只读属性。
  • 接口可以用作函数类型。
  • 接口通常用作类类型,在不相关的类之间建立契约。