再學 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 的一個核心就是類型檢查,使用接口進行類型檢查,有效避免類型轉換致使的錯誤,提升了開發效率。接口自己就是面對對象語言中的一個概念,使用接口也能夠更好的進行面對對象編程。

相關文章
相關標籤/搜索