再学 TypeScript (3) 接口

1、概念

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

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

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

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

​ —— 官方文档介绍函数

这里提到一个“鸭式辨型法”,简单的说,鸭式辨型法就是判断对象是否实现接口中的全部方法,若是是那就是认为这个对象实现了接口,不然就认为没有。this

点击查看鸭式辨型具体code

2、简单使用

定义接口 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'

3、 可选属性

有时候,接口内的属性并非全部对象都须要的,或者是针对某种状况才具备该属性,这时候就用上了可选属性:索引

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

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

4、只读属性

假如对象的某法属性定义后是不容许修改的,能够在属性名前用 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 = '王五'  // 没有检测出错误

5、函数类型接口

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

interface Func {
  (arg: number): string
}


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

6、索引类型接口

像是 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'
}

7、接口的继承

经过 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
  }
}

8、总结

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

相关文章
相关标签/搜索