typescript基础篇(3):接口

时间:2022-07-22
本文章向大家介绍typescript基础篇(3):接口,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

3. 接口

关于接口,你可以理解为定义了属性和类型,但是没有定义其它任何东西的构造函数。接口可以用来约束对象,函数乃至代码结构。

3.1 接口的使用

如果你和后端同事正在联调,讨论好的数据类型是这样的:

[{id:1,name:'djtao'},{id:2,name:'dangjingtao'}...]

试问,如何规范这样的一个接口呢?

interface List {
  id: number
  name: string
}

interface Result {
  data: List[]
}

const render = (result: Result) => {
  result.data.forEach((item) => {
    console.log(item)
  })
}

const result = {
  data: [{ id: 1, name: "djtao" }, { id: 2, name: "dangjingtao" }],
}

render(result)

这里我们使用接口来描述一个拥有id和name字段的List对象。并且定义Result是由List组成的数组。那么,一个标准的接口就被定义出来了。

3.2 接口数据处理

如果后端给你的数据加了一个age字段:

const result = {
  data: [{ id: 1, name: "djtao" }, { id: 2, name: "dangjingtao",age:31 }],
}

代码也不会报错。从这里体现了动态语言的特征(鸭子模型),只要满足接口的必要条件,就可以允许。

但是,如果:

render({
  data: [{ id: 1, name: "djtao" }, { id: 2, name: "dangjingtao", age: 31 }],
})

这样又会报错。

这时候就需要类型断言。断言时,render的入参用Result类型s规范。如下两种都是等价的:

// 在react(tsx)中使用容易被混淆
render(<Result>{
  data: [{ id: 1, name: "djtao" }, { id: 2, name: "dangjingtao", age: 31 }],
});

// tsx只能使用这种方式
render({
  data: [{ id: 1, name: "djtao" }, { id: 2, name: "dangjingtao", age: 31 }],
} as Result)

你也可以使用字符串索引签名,在定义List时:

interface List {
  id: number
  name: string
  [prop: string]: any // 允许接受其它属性
}

3.3 接口成员的特性

假设后端接口可能存在age属性,也可能不存在,前端需要根据此属性进行渲染——

interface List {
  id: number
  name: string
}

interface Result {
  data: List[]
}

const render = (result: Result) => {
  result.data.forEach((item) => {
    console.log(item)
    if (item.age) {// 报错
      console.log(item.age)
    }
  })
}

const result = {
  data: [{ id: 1, name: "djtao" }, { id: 2, name: "dangjingtao", age: 31 }],
}

render(result)

如果我给List定义了age,也不行(类型不兼容),这时候可以使用可选属性(加?)

interface List {
  id: number
  name: string
  age?: number
}

这样就符合预期了。

因为id是重要属性,还可以对id设置只读属性(readonly)

interface List {
  readonly id: number
  name: string
  age?: number
}

这时如果尝试修改id,就会报错。

上面的例子,属性个数都是确定的。如果是不确定的,可以使用索引属性

比如:

interface StringArray {
  [index: number]: string
}

let chars: StringArray = ["a", "b"]

表示声明了一个由字符串组成的数组。也可以混用:

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

3.4 用接口定义函数

回顾之前所学:在ts中定义一个函数可以是这样:

const add = (a: number, b: number) => a + b

用接口描述:一个函数,两个入参都是数字,返回的也是数字。

interface Add {
  (x: number, y: number): number
}
// 或者
type Add = (x: number, y: number) => number
let add: Add = (a, b) => a + b

type和interface 多数情况下有相同的功能,就是定义类型。但有一些小区别:type:不是创建新的类型,只是为一个给定的类型起一个名字。type还可以进行联合、交叉等操作,引用起来更简洁。interface:创建新的类型,接口之间还可以继承、声明合并。如果可能,建议优先使用 interface。

接口也可以同时用做返回函数/字符串和数字:

interface Lib {
  (): void
  version: string
  do(): void
}

// 这样实际上声明的是一个单例
// let lib: Lib = (() => {}) as Lib 

// 考虑工厂模式 可以创建多个实例
const getLib = () => {
  let lib: Lib = (() => {}) as Lib
  lib.version = "1.0"
  lib.do = () => {}
  return lib
}