Skip to main content

接口

基本用法

用于抽象一个对象的结构。

interface Person {
name: string;
age: number;
gender: 0 | 1;
}

const p: Person = {
name: "kll",
age: 18,
gender: 1,
};

鸭子辨型法

这是 TypeScript 判定一个对象是否符合接口规定的一种机制,记住下面这个例子即可。

interface LabeledValue {
label: string;
}
function printLabel(labeledObj: LabeledValue) {
console.log(labeledObj.label);
}

let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj); // OK

myObj 这个对象虽然不符合 LabledValue 接口,但是具有 label 属性,因此它也可以正常作为参数传入 printLable 函数中,但是在 printLabel 函数中无法获取到 myObj 的 size 属性了。

可选属性与只读属性

interface Person {
readonly name: string;
readonly age: number;
gender?: 0 | 1;
}

const p: Person = {
name: "kll",
age: 18,
gender: 1,
};

p 对象中的 nameage 为只读属性,如果修改它们的值会导致编译错误,gender 属性为可选属性,我们可以不给 p 对象设置 gender 属性。

需要注意 readonly 关键字不仅可以用于修饰对象中的属性,还可以用于修饰 数组类型元组类型,但是不能修饰基础类型。

let readonlyArr: readonly number[] = [1, 2, 3]; // 等价于 let readonlyArr: ReadonlyArray<number> = [1, 2, 3];
let s: readonly string = 123; // error

任意属性

有时候我们希望一个接口中除了包含必选和可选属性之外,还允许有其他的任意属性,这时我们可以使用 索引签名 的形式来满足上述要求。

interface Person {
name: string;
age?: number;
[propName: string]: any;
}

let tom: Person = {
name: 'Tom',
gender: 'male'
};

函数类型

使用接口也可以来定义一个函数类型:

interface SumFun {
(x: number, y: number): number;
}

let sum: SumFun = function (x: number, y: number) {
return x + y;
}

另外,函数本质上也是一个对象,因此我们也可以绑定属性在它上面。

interface SumFun {
(x: number, y: number): number;
displayName?: string;
}

let sum: SumFun = function (x: number, y: number) {
return x + y;
}
sum.displayName = "sum";

可索引类型

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

// equal
type StringArray = string[];

接口继承

使用 extends 关键字进行接口继承,一个接口可以继承多个接口。

interface Shape {
color: string;
}

interface PenStroke {
penWidth: number;
}

interface Square extends Shape, PenStroke {
sideLength: number;
}

类型别名也可以被当作父接口被继承。

type Shape = {
color: string;
}

type PenStroke = {
penWidth: number;
}

interface Square extends Shape, PenStroke {
sideLength: number;
}

需要注意的是使用 extends 关键字进行继承时,如果属性发生冲突(属性名相同、类型不同)就会编译不通过,这点和 type 的 & 运算符不一样。

接口与类型别名区别

接口类型别名
应用范围只能定义对象类型所有类型都可以定义
继承使用 extends 关键字,冲突属性会编译报错使用 & 运算符,冲突属性不会报错
重复定义可重复定义不可重复定义

大部分情况下,接口和类型别名可以相互替换,当然语法会稍有差异。

interface SumFun {
(x: number, y: number): number;
displayName?: string;
}

let sum: SumFun = function (x: number, y: number) {
return x + y;
}
sum.displayName = "sum";

上面的 interface 可以换为 type。

type SumFun = {
(x: number, y: number): number;
displayName?: string;
}

let sum: SumFun = function (x: number, y: number) {
return x + y;
}
sum.displayName = "sum";

类型别名的使用范围更广,它不只用于对象类型,还可以用于其他类型。

类型别名只能定义一次,接口可以定义多次,多次定义的接口属性最终会合并到一个接口中(前提是不存在属性冲突,否者会编译不通过)。