关于 TypeScript 接口,你需要知道的十件事

开发 前端
TypeScript 中的接口是一个非常灵活的概念。除了抽象类的部分行为外,它还经常被用来描述“一个对象的形状”。

TypeScript 中的接口是一个非常灵活的概念。除了抽象类的部分行为外,它还经常被用来描述“一个对象的形状”。

01.必需的属性

定义接口时,需要使用interface关键字:

interface User {
  name: string;
  sex: string;
}
const user: User = {
  name: "Bytefer",
  sex: "male",
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

在上面的代码中,我们定义了一个用户界面。然后定义一个用户变量并将其类型设置为用户类型。

但是,如果我们给用户变量赋值,相关的属性就丢失了。然后,TypeScript编译器会提示相关错误。例如,在下面的代码中,我们在分配时缺少 sex 属性:

那么如何解决上面的错误呢?解决方案之一是使用 ? 在定义接口时声明一些属性是可选的。

02.可选属性

interface User {
  name: string;
  sex?: string;
}
let user: User = { // OK
  name: "Bytefer",
};
user = { // Ok
  name: "Bytefer",
  sex: "male",
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

既然不允许缺少属性,那么可以添加未声明的属性吗?

图片

从上图可以看出,使用对象字面量赋值时,包括未声明的age属性,也会报错。解决此问题的最简单方法是向 User 类型添加一个 age 属性:

interface User {
  name: string;
  sex?: string;
  age: number;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

这种方案虽然可以解决问题,但是如果我们想添加其他任意属性,这种方式就不太好了。为满足上述要求,我们可以使用索引签名。

03.索引签名

索引签名的语法如下:

键的类型只能是字符串、数字、符号或模板字面量类型,而值的类型可以是任何类型。

现在我们了解了索引签名的语法,让我们更新用户类型:

interface User {
  name: string;
  sex?: string;
  [propName: string]: any; // Index Signatures
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

更新 User 类型,并添加新的 age 和 email 属性后,TypeScript 编译器不会提示错误。

let user: User = {
  name: "Bytefer",
  sex: "male",
  age: 30,
  email: "bytefer@gmail.com"
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

04.只读属性

在web系统中,我们需要区分不同的用户,一般情况下,我们会使用一个id属性来标识不同的用户。该属性由Web系统自动生成,用户无法修改。对于上面的场景,我们可以使用readonly修饰符来定义只读属性。

除了属性之外,对象还可能包含方法。在使用接口定义对象类型时,我们还可以同时声明对象上存在的方法:

interface User {
  id: string;
  name: string;
  say(words: string): void;
}
let user: User = {
  id: "6666",
  name: "Bytefer",
  say(words: string) {
    console.log(words);
  },
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

05.Call Signatures

描述函数的最简单方法是使用函数类型表达式,这些类型在语法上类似于箭头函数:

const log: (msg: string) => void = (msg: string) => {
  console.log(msg);
};
log("Bytefer");
  • 1.
  • 2.
  • 3.
  • 4.

语法 (msg: string) => void 的意思是“一个函数,它有一个名为 msg 的参数,类型为字符串,没有返回值”。当然,我们可以使用类型别名来命名函数类型:

type LogFn = (msg: string) => void;
const log: LogFn = (msg: string) => {
  console.log(msg);
};
  • 1.
  • 2.
  • 3.
  • 4.

如果我们想描述一些可以用属性调用的东西,函数本身也是一个对象。那么函数类型表达式不能满足这个要求。对于这种场景,我们可以在定义对象类型时使用调用签名:

图片

需要注意的是,在声明调用签名时,也支持重载:

interface Logger {
  type: string;
  (msg: string): void;
  (msg: string, timestamp: number): void
  (msg: string, timestamp: number, module: string): void
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

06.构建签名

除了直接调用函数,我们还可以使用new运算符来调用函数,一般称为构造函数。我们可以通过在调用签名前添加 new 关键字来编写构造签名:

interface PointConstructor {
  new (x: number, y: number): { x: number; y: number };
}
function createPoint(ctor: PointConstructor, 
  x: number = 0, y: number = 0) {
  return new ctor(x, y);
}
class Point {
  constructor(public x: number, public y: number) {}
}
const zero = createPoint(Point);
console.log(zero);
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

07.混合类型

那么在定义接口的时候,是否可以同时使用调用签名和构造签名呢?答案是肯定的,我们常用的Date对象,它的类型是DateConstructor,其中调用签名和构造签名都用到了:

declare var Date: DateConstructor;
  • 1.

在上面的代码中,除了调用签名和构造签名外,还定义了 Date 构造函数上的属性和方法。

08.通用接口

通用类型也可以与接口一起使用。下面是一个通用接口。

interface KeyPair<T, U> {
  key: T;
  value: U;
}
let kv1: KeyPair<number, string> = { key: 1, value: "Bytefer" };
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

09.扩展接口

接口可以扩展一个或多个接口,这使得编写接口灵活且可重用。

interface Point1D {
  x: number;
}
interface Point2D extends Point1D {
  y: number;
}
interface Point3D extends Point2D {
  z: number;
}
const point1D = { x: 0 };
const point2D = { x: 0, y: 0 };
const point3D = { x: 0, y: 0, z: 0 };
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

除了扩展单个接口,TypeScript 还允许我们扩展多个接口:

interface CanSay {
   say(words: string) :void 
}
interface CanWalk {
  walk(): void;
}
interface Human extends CanSay, CanWalk {
  name: string;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

10.扩展类

当声明一个接口时,我们可以扩展一个或多个接口。其实我们也可以扩展一个声明的类:

class Point1D {
  public x!: number;
}
interface Point2D extends Point1D {
  y: number;
}
const point2D: Point2D = { x: 0, y: 0 }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

对于一个类,在声明类的时候,可以同时实现多个接口:

interface CanSay {
   say(words: string) :void 
}
interface CanWalk {
  walk(): void;
}
class Person implements CanSay, CanWalk {
  constructor(public name: string) {}
  public say(words: string) :void {
    console.log(`${this.name} says:${words}`);  
  }
  public walk(): void {
    console.log(`${this.name} walk with feet`);
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

对于 TypeScript 开发者来说,接口和类型有很多相似之处,当然也有一些不同。

责任编辑:华轩 来源: web前端开发
相关推荐

2015-09-08 11:47:00

微软容器

2023-06-30 13:35:12

2023-04-10 16:02:59

2023-04-04 14:20:27

2017-05-02 11:36:00

Java

2010-09-27 09:24:09

云计算

2024-10-14 12:42:06

2020-08-10 15:30:24

XDR网络安全网络威胁

2015-10-10 09:29:32

GitGithub

2025-02-10 08:59:54

2015-10-10 13:11:35

GitGithub工具

2023-05-18 15:50:59

Arch Linux命令

2021-04-15 08:04:27

容器DevOps程序

2015-08-11 17:55:21

谷歌重组科技

2015-10-26 16:34:08

安装Ubuntu 15.1Linux

2022-10-09 16:35:08

Redis开发集群

2010-08-09 13:13:00

Flex程序员

2015-03-04 14:54:47

DockerIT管理基础设施

2009-05-26 09:48:34

2013-08-30 15:53:32

Java报错
点赞
收藏

51CTO技术栈公众号