TypeScript(二)接口

TypeScript 的核心就是對(duì)值所具有的結(jié)構(gòu)進(jìn)行類型檢查。接口規(guī)定了行為和規(guī)范长搀,起到限制和規(guī)范的作用宇弛。

接口使用 interface 關(guān)鍵字聲明。一般首字母大寫源请,根據(jù) ts 命名規(guī)范枪芒,接口名加前綴 I

本文包括對(duì)象類型谁尸、函數(shù)類型舅踪、可索引的類型、混合類型的接口以及繼承接口良蛮。

對(duì)象類型的接口

在函數(shù)調(diào)用時(shí)對(duì)傳入的對(duì)象屬性進(jìn)行檢查限制抽碌,可以通過(guò)以下方式:

function getFoodInfo({ name, color }: { name: string; color: string }): string {
  return `the ${name} color is ${color}!`
}

通過(guò)接口來(lái)描述上例:

interface IFood {
  name: string
  color: string
}

function getFoodInfo({ name, color }: IFood): string {
  return `the ${name} color is ${color}!`
}

getFoodInfo({
  name: 'cake',
  color: 'white'
})

類型檢查器不會(huì)檢查屬性的順序,只要相應(yīng)的屬性存在并且類型一致即可决瞳。

可選屬性

接口中的屬性并不都是必需的货徙,有些屬性在某些特定條件下存在。

帶有可選屬性的接口只是在可選屬性名字定義的后面加一個(gè) ? 符號(hào)皮胡。

interface AreaConfig {
  name: string
  width?: number
}

// width 屬性為可選的
function getAreaInfo({ name, width }: AreaConfig): {
  name: string
  square: number
} {
  const area = { square: 9, name }

  if (width) area.square = width * width
  return area
}

getAreaInfo({ name: 'beijing' })

可選屬性的好處:

  1. 可以對(duì)可能存在的屬性進(jìn)行預(yù)定義
  2. 提前捕獲引用了不存在的屬性時(shí)拋出的錯(cuò)誤

只讀屬性

有些對(duì)象的屬性在對(duì)象創(chuàng)建之后是不可修改的痴颊。可以在屬性名前用 readonly 來(lái)指定只讀屬性:

對(duì)象屬性

interface Food {
  readonly name: string
  color: string
}

const tomato: Food = {
  name: 'tomato',
  color: 'red'
}
tomato.name = 'potato' // Cannot assign to 'name' because it is a read-only property.

不可變數(shù)組

ReadonlyArray<T> 可以創(chuàng)建一個(gè)不可變的數(shù)組胸囱,ReadonlyArray<T> 類型與 Array<T> 相似祷舀,只是把所有可變方法去掉了,因此可以確保數(shù)組創(chuàng)建后再也不能被修改:

定義一個(gè)普通數(shù)組:

let arr: Array<number> = [1, 2, 3]
arr[1] = 4
console.log(arr) // [1, 4, 3]

創(chuàng)建一個(gè)不可改變的數(shù)組:

let array: ReadonlyArray<number> = [5, 6, 7, 8]

不可變數(shù)組的特性:

  1. 不可修改某一項(xiàng):
array[1] = 55
// Index signature in type 'readonly number[]' only permits reading.
  1. 不可刪除、添加元素
array.push(7)
array.splice(0, 1)
// Property 'splice' does not exist on type 'readonly number[]'.
  1. 不可修改數(shù)組 length 屬性
array.length = 6
// ? Cannot assign to 'length' because it is a read-only property.
  1. 不可以把 ReadonlyArray 數(shù)組賦值給一個(gè)普通數(shù)組
arr = array
// ? The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.

用類型斷言可以進(jìn)行賦值:

arr = array as number[]
arr[1] = 555

console.log(array === arr) // true
// ? array 和 arr 指向同一個(gè)引用 裳扯,并且可以通過(guò)arr去更改array的元素抛丽,但是仍然不可以直接通過(guò)array修改數(shù)組的元素

array[1] = 111 // Index signature in type 'readonly number[]' only permits reading.

注意:readonly 聲明的只讀數(shù)組類型與 ReadonlyArray 聲明的只讀數(shù)組類型,二者等價(jià)饰豺。

const arr1: readonly number[] = [1, 2]
const arr2: ReadonlyArray<number> = [1, 2, 3]

readonly vs const 的區(qū)別

const 定義的變量不可重新賦值亿鲜,但是其屬性值是可改變的,而 readonly 定義的屬性不可改變冤吨。

類型兼容

TypeScript 的核心原則之一是對(duì)值所具有的結(jié)構(gòu)進(jìn)行類型檢查蒿柳。 它有時(shí)被稱做鴨式辨型法或結(jié)構(gòu)性子類型化。

鴨式辨型來(lái)自于 James Whitecomb Riley 的名言:"像鴨子一樣走路并且嘎嘎叫的就叫鴨子漩蟆。" 通過(guò)制定規(guī)則來(lái)判定對(duì)象是否實(shí)現(xiàn)這個(gè)接口垒探。

看以下例子:

const info = { age: 3, name: 'jack' }
function getInfo(info: { name: string }): string {
  return info.name
}
getInfo(info) // ?
getInfo({ name: 'jack', age: 3 }) // ?

通過(guò)接口約束:

interface Info {
  name: string
}
function getInfo(info: Info): string {
  return info.name
}
getInfo(info) // ?
getInfo({ name: 'jack', age: 3 }) // ?

為什么賦值會(huì)使得類型檢測(cè)變得不嚴(yán)格呢?

因?yàn)樵趨?shù)中直接賦值怠李,對(duì)象會(huì)有嚴(yán)格的類型檢查圾叼。而將對(duì)象賦值給變量 info,該變量會(huì)推斷為 info: {name: string; age: number;} = {name: 'jack', age: 3 }捺癞,然后再將變量賦值給參數(shù)夷蚊,參照鴨式辨型法,兩種類型的對(duì)象都具有 name 屬性髓介,所以被認(rèn)為是相同的惕鼓,進(jìn)而可以繞開多余屬性的檢查。

額外屬性檢查

所謂額外屬性是指對(duì)象中的屬性沒有在接口中定義過(guò)唐础。

比如:

interface Food {
  name: string
  size: number
}

function getFood(info: Food): string {
  return `${info.name} size is ${info.size}, color is ${info.color}`
}

getFood({ name: 'tomato', size: 10, color: 'red' }) // ?
// Argument of type '{ name: string; size: number; color: string; }' is not assignable to parameter of type 'Food'.
// Object literal may only specify known properties, but 'color' does not exist in type 'Food'.

在調(diào)用 getFood 時(shí) 傳入的參數(shù)多了一個(gè) color 屬性箱歧,而接口中沒有定義 color,ts 類型檢查器檢測(cè)出 color 這個(gè)額外屬性從而會(huì)拋出錯(cuò)誤彻犁。

繞開 ts 對(duì)額外屬性的檢查有三種方式:

1. 類型斷言(最簡(jiǎn)便的方式)

getFood({ name: 'tomato', size: 10, color: 'red' } as Food)

2. 字符串索引簽名(最佳方式)

interface Food {
  name: string
  size: number
  [key: string]: any
}

3. 類型兼容(將對(duì)象賦值給另一個(gè)變量)

const obj = { name: 'tomato', size: 10, color: 'red' }
getFood(obj)

可索引的類型

可索引的類型描述的是可以通過(guò)索引得到的類型叫胁。比如 arr[1] 或 person.name。說(shuō)白了就是數(shù)組或?qū)ο笞裱囊?guī)范和約束汞幢。可索引類型具有一個(gè) 索引簽名微谓,描述了索引的類型以及值的類型森篷。

比如下例用接口約束數(shù)組:StringArray 接口的索引簽名類型為 number,索引簽名值的類型為 string

interface StringArray {
  [index: number]: string
}
const arr: StringArray = ['red', 'pink']

索引簽名有兩種形式:數(shù)字和字符串

1. 數(shù)字:
interface Arr {
  [index: number]: number
}
const arr: Arr = [1, 2, 3]
2. 字符串:
interface PropString {
  [prop: string]: number
}

const objStr: PropString = {
  success: 1,
  failed: 0
}

將索引簽名設(shè)置為只讀豺型,可以防止給索引賦值:

interface Sina {
  readonly [index: number]: string
}
const sian: Sina = {
  1: 'sina'
}
sian[1] = 'xiaomi' // ? Index signature in type 'Sina' only permits reading

注意:一旦定義了可索引簽名仲智,則必選屬性和可選屬性的值的類型都必須是可索引類型值的子類型,因?yàn)楸剡x屬性與可選屬性也索引屬性的一種:

interface Person {
  name: boolean // ?
  age: number // ?
  sex?: string // ?
  skill: undefined // ?
  [key: string]: string
}

可同時(shí)使用兩種類型的索引姻氨,但是數(shù)字類型的索引對(duì)應(yīng)的值必須是字符串類型索引對(duì)應(yīng)的值的子類型钓辆,因?yàn)楫?dāng)使用數(shù)字作為索引時(shí),javascript 會(huì)將數(shù)字轉(zhuǎn)成字符串再去索引對(duì)象

如下例:string 類型屬性的返回值為 string 類型,所以 number 類型屬性的返回值必須為 string 的子類型(包括 string)

// ?
interface PropType {
  [id: number]: string
  [propName: string]: string
}

// ?
interface PropType {
  [id: number]: number
  [propName: string]: any
}

// ? Numeric index type 'number' is not assignable to string index type 'string'.
interface PropType {
  [id: number]: number
  [propName: string]: string
}

當(dāng)索引屬性使用聯(lián)合類型且接口中存在可選屬性時(shí)前联,需要聯(lián)合 undefined 類型功戚,否則編譯報(bào)錯(cuò),因?yàn)榭蛇x屬性是額外屬性的字類型:

interface Info {
  name: string
  age?: number // age的類型實(shí)際為:number | undefined
  [key: string]: string | number | undefined
}

const info: Info = {
  name: 'jack',
  age: 2
}
// ?

函數(shù)類型

接口除了可以描述帶有屬性的普通對(duì)象外似嗤,還可以描述函數(shù)類型啸臀。

使用接口表示函數(shù)類型時(shí),需要給接口定義一個(gè)調(diào)用簽名烁落,用來(lái)描述函數(shù)的參數(shù)和返回值的類型乘粒。如下:

interface FuncInterface {
  (num1: number, num2: number): number
}

const add: FuncInterface = (n1, n2) => n1 + n2

add(2, 5)

參數(shù)名不需要與接口里定義的參數(shù)名相同。此外由于 TypeScript 的類型系統(tǒng)會(huì)推斷出參數(shù)類型伤塌,所以參數(shù)類型可以不指定灯萍。函數(shù)的返回值類型會(huì)通過(guò)返回值推斷出來(lái),也可以不指定每聪。

繼承接口

接口可以通過(guò) extends 關(guān)鍵字實(shí)現(xiàn)繼承旦棉。

interface BaseInfo {
  name: string
  age: number
}

interface AddressInfo extends BaseInfo {
  address: string
}

const userInfo: AddressInfo = {
  name: 'jack',
  age: 12,
  address: 'shanghai'
}

繼承多個(gè)接口:

interface AllInfo extends BaseInfo, AddressInfo {
  hobby: string
}

const allInfo: AllInfo = {
  name: 'mike',
  age: 12,
  address: 'beijing',
  hobby: 'song'
}

混合類型

函數(shù)也是對(duì)象,也有自己的屬性和方法熊痴。

通過(guò) TypeScript 混合類型接口定義他爸。

interface Counter {
  // 函數(shù)參數(shù)和返回值類型
  (name: string): void
  // 函數(shù)屬性及類型
  count: number
  // 函數(shù)方法及返回值
  rest(): void
}

// 定義一個(gè)函數(shù),該函數(shù)返回Counter類型的函數(shù)
function getCounter(): Counter {
  const getCount = (name: string) => {
    getCount.count++
  }

  getCount.count = 0
  getCount.rest = function () {
    this.count = 0
  }

  return getCount
}

const getCount = getCounter()
getCount() // 1
getCount() // 2
getCount.rest() // 0
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末果善,一起剝皮案震驚了整個(gè)濱河市诊笤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌巾陕,老刑警劉巖讨跟,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異鄙煤,居然都是意外死亡晾匠,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門梯刚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)凉馆,“玉大人,你說(shuō)我怎么就攤上這事亡资±焦玻” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵锥腻,是天一觀的道長(zhǎng)嗦董。 經(jīng)常有香客問(wèn)我,道長(zhǎng)瘦黑,這世上最難降的妖魔是什么京革? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任奇唤,我火速辦了婚禮,結(jié)果婚禮上匹摇,老公的妹妹穿的比我還像新娘咬扇。我一直安慰自己,他們只是感情好来惧,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布冗栗。 她就那樣靜靜地躺著炒事,像睡著了一般蔬顾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瞎惫,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天葛虐,我揣著相機(jī)與錄音胎源,去河邊找鬼。 笑死屿脐,一個(gè)胖子當(dāng)著我的面吹牛涕蚤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播的诵,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼万栅,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了西疤?” 一聲冷哼從身側(cè)響起烦粒,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎代赁,沒想到半個(gè)月后扰她,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡芭碍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年徒役,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窖壕。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡忧勿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瞻讽,到底是詐尸還是另有隱情狐蜕,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布卸夕,位于F島的核電站,受9級(jí)特大地震影響婆瓜,放射性物質(zhì)發(fā)生泄漏快集。R本人自食惡果不足惜贡羔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望个初。 院中可真熱鬧乖寒,春花似錦、人聲如沸院溺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)珍逸。三九已至逐虚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谆膳,已是汗流浹背叭爱。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留漱病,地道東北人买雾。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像杨帽,于是被迫代替她去往敵國(guó)和親漓穿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350