一、概念

在面对对象语言中,接口(Interface)是个很重要的概念,利用接口实现多态。在 TypeScript 中也引入了接口的概念。

前面在整理 TypeScript 中的基本类型的时候说了对于基础类型以及数据的类型注解,还少了一个很重要的 Object ,一般就是使用接口。通过接口描述一个对象的相关属性和方法,使得 TypeScript 的类型检查可以在我们开发的时候对其进行检测提示。

这里要有别于其他面对对象语言的接口, TypeScript 中的类型只是用作类型检测,在最终编译成 js 后会移除接口。

TypeScript的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在 TypeScript 里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。

​ —— 官方文档介绍

这里提到一个“鸭式辨型法”,简单的说,鸭式辨型法就是判断对象是否实现接口中的所有方法,如果是那就是认为这个对象实现了接口,否则就认为没有。

点击查看鸭式辨型具体

二、简单使用

定义接口 Time ,同时定义了一些属性和方法,编译器会检查对象是否具有接口中定义的属性或方法,并且类型一致。

interface Time {
  hour: number
  minute: number
  second: number
}
const time: Time = {
  hour: 9,
  minute: 0,
  second: 0
}

const bad1: Time = {
  hour: '9',  // 不能将类型“string”分配给类型“number”。
  minute: 0,
  second: 0
}

const bad2: Time = {  // error 缺少属性 "second"
  hour: 9,
  minute: 0
}

const bad3: Time = {  // error 缺少属性 "timestamp"
  hour: 9,
  minute: 0,
  second: 0,
  timestamp: '09:00:00'  // error “timestamp”不在类型“Time”中
}

这里要注意一种情况,当给函数的参数进行注解的时候,只要具有接口的属性就不会报错,这个在指定参数属性的类型时很方便。

const time = {
  hour: 9,
  minute: 0,
  second: 0,
  timestamp: '09:00:00'
}

function getTime(time: { timestamp: string }): string {
  return time.timestamp
}

getTime(time) // return '09:00:00'

三、 可选属性

有时候,接口内的属性并不是所有对象都需要的,或者是针对某种情况才具有该属性,这时候就用上了可选属性:

interface User {
  id: number
  chineseName: string
  englishName?: string
}

const user1: User = {
  id: 0,
  chineseName: '张三'
}

四、只读属性

假如对象的某法属性定义后是不允许修改的,可以在属性名前用 readonly 来指定它为只读。修改下上面的示例,将 id 变成只读,可以发现,在定义后不能再修改 id 的值了,这就避免了实际开发中我们在不经意间修改了应该保持不变的属性,减少错误情况。

interface User {
  readonly id: number
  chineseName?: string
  englishName?: string
}

const user: User = {
  id: 0,
  chineseName: '张三'
}

user.chineseName = '李四'
user.id = 1  // error 无法分配到 "id" ,因为它是只读属性 ,

如果只读属性并不是基础类型呢,是引用类型的情况,比如数组或者对象,就有点不一样了,只有再对只读属性进行赋值的时候会检测出错误来:

interface User {
  readonly id: number
  chineseName?: string
  englishName?: string
  readonly friends?: User[]
}

const user: User = {
  id: 0,
  chineseName: '张三',
  users: [{
    id: 1,
    chineseName: '李四'
  }]
}

user.friends = []  // error 无法分配到 "friends" ,因为它是只读属性。
user.friends = [{}]  // error 无法分配到 "friends" ,因为它是只读属性。
user.friends[0].chineseName = '王五'  // 没有检测出错误

五、函数类型接口

TypeScript 中的接口除了描述对象类型之外,还可以描述函数的类型。和普通对象的接口一样,一样是通过 key-value 来描述函数的参数和返回,函数的参数名可以和接口定义的参数名不一样,函数的参数会逐个检查,只要参数的类型与接口定义的一致就可以。

interface Func {
  (arg: number): string
}


const fn: Func = function(num) {
  return num.toFixed(2)
}

六、索引类型接口

像是 array[0]obj[prop] 这些也可以通过索引类型来描述。

interface IData {
  // [索引签名: 索引签名的类型]: 索引返回值类型 
  [index: number]: string
}
const data: IData = ['8', '9', '10']

TypeScript 支持字符串和数字两种索引签名,其实也就是对象数组这两种:

interface IArr {
  [key: number]: string
}

interface IObj {
  [key: string]: string
}

const arr: IArr = ['a', 'b', 'c']
const obj: IObj = {
  a: '1',
  b: '2',
  c: '3'
}

七、接口的继承

通过 extends (继承)可以将一个接口的成员复制到另一个接口。

interface INum {
  num: number
}

interface IStr {
  str: string
}

// 可以同时继承多个
interface IComb extends INum, IStr {
  handle: () => void
}

const obj: IComb = {
  num: 0,
  str: 'some',
  handle: () => { console.log('function') }
}
接口继承类

接口也可以继承类。需要注意的是接口只是继承类中的所有成员,不包括其实现,如果类具有 private 或 protected 成员,同样也会被接口继承,但是这个接口就只能被这个类或其子类实现。

class Factory {
  private state
  protected name
}

interface IFactory extends Factory {
  getName(): any
}

class factory extends Factory implements IFactory {
  getName() {
    return this.name
  }
}

// 类型“bad”缺少类型“IFactory”中的以下属性: state, name
class bad implements IFactory {
  getName() {
    return this.name
  }
}

八、总结

TypeScript 的一个核心就是类型检查,使用接口进行类型检查,有效避免类型转换导致的错误,提高了开发效率。接口本身就是面对对象语言中的一个概念,使用接口也可以更好的进行面对对象编程。


这个我不会啊
16 声望0 粉丝