TS - Interfaces詳解

一讲坎、什么是接口

One of the core principles of typescript is to type check the shape the value has. It is sometimes called "duck type discrimination" or "structural subtype". In typescript, the function of an interface is to name these types and define contracts for your code or third-party code.

  • 常用于對(duì)對(duì)象的形狀(Shape)進(jìn)行描述泉坐。
  • 可用于對(duì)類的一部分行為進(jìn)行抽象
  • 為類型命名和為你的代碼或第三方代碼定義契約。

二、對(duì)象與接口

2.1 對(duì)象接口簡(jiǎn)例

接口一般首字母大寫苍柏。tslint建議加上I前綴, 否則會(huì)發(fā)出警告,可在tslint中關(guān)閉它,在rules添加規(guī)則 ["interface-name":[true,"nerver-prefix"]

interface Person {
  name: string,
  age: number
}
const why: Person = {
  name: 'why',
  age: 18
}

首先我們定義了一個(gè)接口Person,接口約束了shape必須要有name,age兩個(gè)屬性,并且有對(duì)應(yīng)的數(shù)據(jù)類型,然后我們創(chuàng)建了 why對(duì)象面向Person


定義的變量比接口少一些屬性是不被允許的:
IDE 添加了tslint插件會(huì)發(fā)出警告,編譯和瀏覽器控制臺(tái)會(huì)報(bào)錯(cuò)

interface Person {
  name: string,
  age: number
}
const why: Person = {
  name: 'why',
}
// Property 'age' is missing in type '{ name: string; }' but required in type 'Person'.

屬性溢出也是不允許的:

interface Person {
  name: string,
  age: number
}
const why: Person = {
  name: 'why',
  age: 18,
  gender: 'female'
}

需要注意接口不能轉(zhuǎn)換為 JavaScript画切。 它只是 TypeScript 的一部分。
可在TypeScript學(xué)習(xí)樂(lè)園實(shí)時(shí)轉(zhuǎn)換TypeScript

2.2 可選屬性與只讀屬性

如果希望不要完全匹配形狀长踊,那么可以用可選屬性:?
如果希望對(duì)象中的一些字段只讀功舀,那么可以用 readonly 定義

interface Person {
  name?: string,
  readonly age: number,
  run?(): void,
}
const why: Person = {
  // name 鍵值對(duì)缺失不會(huì)報(bào)錯(cuò)
  age: 18
}
why.run&&why.run() //判斷函數(shù)執(zhí)行
why.age++  //修改age報(bào)錯(cuò) Cannot assign to 'age' because it is a read-only property.

2.3 任意屬性

如果希望一個(gè)接口有任意的屬性,可以使用如下方式
(1) 可索引類型

interface Person {
  name: string,
  age?: number,
  [attr: string]: any
}
const why: Person = {
  name: 'why',
  age: 18,
  gender:'female'
}

需要注意身弊,定義了任意屬性辟汰,那么確定屬性和可選屬性的類型都必須是它的類型的子集,否則會(huì)出現(xiàn)unexpected error.

interface Person {
  name: string,
  age?: number,
  [attr: string]: string // error
}
const why: Person = {
  name: 'why',
  age: 18,
  gender:'female'

  /* Type '{ name: string; age: number; gender: string; }' is not assignable to type 'Person'.
   Property 'age' is incompatible with index signature.
   Type 'number' is not assignable to type 'string'. */
}

(2) 類型斷言

有兩種方法

interface Person {
  name: string,
  age?: number,
}
const why: Person = <Person>{
  name: 'why',
  age: 18,
  gender:'female'
} 

當(dāng)然要配置 tslint "no-angle-bracket-type-assertion":false否則tslint會(huì)報(bào)錯(cuò),因?yàn)樗扑]你使用第二種方式

interface Person {
  name: string,
  age?: number,
}
const why: Person = {
  name: 'why',
  age: 18,
  gender:'female'
} as Person

(3) 類型兼容性

interface IUser {
  name: string,
  age: number,
}
type IUserFunc = (user: IUser) => string

const foo: IUserFunc = (user: IUser) => {
  return user.name
}

foo({ name: 'clc', age: 18, gender: 'male' }) //error
// TS2345: Argument of type '{ name: string; age: number; gender: string; }' is not assignable to parameter of type 'IUser'.
//Object literal may only specify known properties, and 'gender' does not exist in type 'IUser'.

const bar = { name: 'clc', age: 18, gender: 'male' }
foo(bar)
// 用變量bar 接收參數(shù)再傳入正常編譯

直接傳參報(bào)錯(cuò),但用變量bar接收再傳入正常編譯

interface IUser {
  name: string,
  age: number,
}

const user: IUser = {} // missing the following properties from type 'IUser': name, age

const user = {} as IUser // right

2.4 聯(lián)合類型

interface UnionType {
  bar: any[] | (() => void) | object
}
const foo:UnionType = {
  bar:['w','h','y']
// bar 是一個(gè)any類型的數(shù)組
}
const foo:UnionType = {
  bar() {}
// bar 是一個(gè)返回值為void的函數(shù)
}
const foo:UnionType = {
  bar: {}
// bar 是一個(gè)對(duì)象
}

2.5 可索引類型

可索引類型內(nèi)有一個(gè)索引簽名,它表示了索引的類型,還有索引值對(duì)應(yīng)的值的類型

2.5.1 數(shù)字索引簽名

index 作為簽名占位,可為任意字符.

interface NumMap {
  [index: number]: string
}
const obj: NumMap = {
  0: 'foo',
  1: 'bar',
  '2': 'why',
  'c': 'clc' //errorType  is not assignable to type 'NumMap'.Object literal may only specify known properties, and ''c'' does not exist in type 'NumMap'.
}
obj[0] // foo
obj[2] // why

數(shù)字作為對(duì)象的鍵是轉(zhuǎn)換為字符形式的,key為數(shù)字字符串正常編譯,若為非數(shù)字字符串,報(bào)錯(cuò).


數(shù)組本身索引簽名即為number 類型

interface NumMap {
  [index: number]: string
}
let arr: NumMap
arr = ['foo', 'bar']
2.5.2 字符索引簽名
interface StrMap {
  [str: string]: string
}
let obj: StrMap = {
  foo: 'foo',
  bar: 'bar',
  1: 'why'  // 正常編譯
}
const arr: StrMap = ['foo','bar'] // error 缺少字符索引簽名

字符索引簽名類型包含一部分?jǐn)?shù)字索引簽名類型


可同時(shí)使用兩種索引類型,但是數(shù)字索引的返回值必須是字符索引返回值的子類型

interface StrNumMap {
  [str: string]: any,  // any
  [index:number]: string
}
let obj: StrNumMap = {
  foo: 'foo',
  bar: 'bar',
  1: 'why'
}
const arr: StrNumMap = ['foo','bar'] // 正常編譯

2.6 可同時(shí)面向多個(gè)接口

const user: IUser | IStudent = {}

三、函數(shù)與接口

3.1 TypeScript函數(shù)存在的問(wèn)題

為了防止錯(cuò)誤調(diào)用,給函數(shù)的參數(shù)和返回值定義了類型限制,但代碼長(zhǎng)不方便閱讀

const foo = (name: string, age: number): string => {
  return `name : ${name}, age: ${age}`
}
3.1.1 使用接口重構(gòu)一
interface IUser {
  name: string,
  age: number,
}

const foo = ({name,age}:IUser): string => {
  return `name : ${name}, age: ${age}`
}

還可以把參數(shù)對(duì)象替換成一個(gè)變量user

interface IUser {
  name: string,
  age: number,
}

const foo = (user:IUser): string => {
  return `name : ${user.name}, age: ${user.age}`
}
3.1.2 使用接口重構(gòu)二

定義函數(shù)還可以寫成如下形式

const foo: (name: string, age: number) => string = (name, age) => {
  return `name : ${name}, age: ${age}`
}
// 給一個(gè)變量foo定義了一個(gè)函數(shù)類型,并且把一個(gè)函數(shù)賦值給了foo

接著定義兩個(gè)接口

interface IUser {
  name: string,
  age: number,
}
interface IUserFunc {
  (user:IUser):string
}
// warn Interface has only a call signature — use `type IUserFunc = // (user:IUser) =>string` instead.

第二個(gè)接口會(huì)報(bào)警告,tslint建議您如果一個(gè)函數(shù)接口只有一個(gè)方法,建議使用類型別名,type語(yǔ)句, 可配置["callable-types":false] 關(guān)掉它,或者聽(tīng)它的吧

type IUserFunc = (user: IUser) => string

接下來(lái)按步重構(gòu)函數(shù)

interface IUser {
  name: string,
  age: number,
}
type IUserFunc = (user: IUser) => string

const foo: (name: string, age: number) => string = (name, age) => {
  return `name : ${name}, age: ${age}`
}

面向第一個(gè)接口

const foo: (user:IUser) => string = (user) => {
  return `name : ${user.name}, age: ${user.age}`
}

next

const foo: IUserFunc = user => {
  return `name : ${user.name}, age: ${user.age}`
}

函數(shù)簡(jiǎn)短了許多,并且看接口就可以知道函數(shù)參數(shù),返回值的類型限制

3.2 混合類型

interface Counter {
  count: number,
  ():void
}
function getFunc(): Counter {
  function fn(){
    fn.count++
  }
  fn.count = 0
  return fn
}

函數(shù)getFunc返回類型為混合類型,即做為函數(shù)和對(duì)象使用阱佛,并帶有額外的屬性帖汞。

四、類與接口

接口對(duì)類的一部分行為進(jìn)行抽象凑术。

4.1 類實(shí)現(xiàn)接口

實(shí)現(xiàn)(implements)是面向?qū)ο笾械囊粋€(gè)重要概念翩蘸。一個(gè)類只能繼承自另一個(gè)類,不同類之間可以有一些共有的特性麦萤,就可以把特性提取成接口(interfaces)鹿鳖,用 implements 關(guān)鍵字來(lái)實(shí)現(xiàn)扁眯。


(1)TypeScript中定義一個(gè)基本的類

class Person {
  name: string
  run:Function
  constructor(name: string) {
    this.name = name
    this.run=()=>{}
  }
}
class Rapper extends Person {
  age: number
  constructor(name: string, age: number) {
    super(name)
    this.age = age
  }
  rap(){
    console.log('I can sing and dance tap');
  }
}

(2) implements 關(guān)鍵字實(shí)現(xiàn)接口

interface Skr {
  name: string,
  age: number,
  rap():void
}
class Person {
  name: string
  constructor(name: string) {
    this.name = name
  }
}
class Rapper extends Person implements Skr{
  age: number
  constructor(name: string, age: number) {
    super(name)
    this.age = age
  }
  rap(){
    console.log('I can sing and dance tap');
  }
}

雖然定義了一個(gè)接口,但是在接口類中的屬性和方法還是要寫類型限制.感覺(jué)代碼反而應(yīng)為接口冗余了.
但類的接口是一種規(guī)范,因?yàn)榻涌诜蛛x了規(guī)范和實(shí)現(xiàn),可通過(guò)接口輕易解讀類必須提供的屬性和方法,增強(qiáng)了可拓展性和可維護(hù)性.


(3) 一個(gè)類可實(shí)現(xiàn)多個(gè)接口

interface Skr {
  name: string,
  age: number,
  rap():void
}
interface SkrSkr{
  sing():void
}
class Person {
  name: string
  constructor(name: string) {
    this.name = name
  }
}
class Rapper extends Person implements Skr,SkrSkr{
  age: number
  constructor(name: string, age: number) {
    super(name)
    this.age = age
  }
  rap(){
    console.log('I can sing and dance tap');
  }
  sing(){
    console.log('I can sing and dance tap');
  }
}

五、接口繼承

  • 接口可以通過(guò)其他接口來(lái)擴(kuò)展自己翅帜。
  • Typescript 允許接口繼承多個(gè)接口姻檀。
  • 繼承使用關(guān)鍵字 extends。

5.1 單接口繼承

extends 關(guān)鍵字后加繼承的接口

interface Person {
  name: string,
  age: number
}
interface Students extends Person {
  gender: string
}
const foo: Students = {
  name: 'why',
  age: 18,
  gender: 'female'
}

5.2 多接口繼承

多接口之間逗號(hào)分隔

interface Sing {
  sing(): void
}
interface Jump {
  jump(): void
}
interface Rap extends Sing, Jump {
  rap(): void
}
const foo: Rap = {
  sing(){},
  jump(){},
  rap(){}
}

5.3 接口繼承類

常見(jiàn)的面向?qū)ο笳Z(yǔ)言中涝滴,接口是不能繼承類的绣版,但是在 TypeScript 中卻是可行的
用extends關(guān)鍵字繼承類

class Person {
  name: string
  age: number
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
  run(): void { }
}
interface User extends Person {
  gender: string
}
const foo: User = {
  name: 'foo',
  age: 18,
  gender: 'male',
  run():void { }
}

本質(zhì)上就是接口繼承接口

interface Person {
  name: string,
  age: number,
  run(): void
}
interface User extends Person {
  gender: string
}
const foo: User = {
  name: 'foo',
  age: 18,
  gender: 'male',
  run(): void { }
}

因?yàn)槁暶?class Person時(shí),除了創(chuàng)建一個(gè)名為 Person的類歼疮,同時(shí)也創(chuàng)建了一個(gè)名為 Person的類型(實(shí)例的類型)杂抽。
所以 Person既可以 當(dāng)做一個(gè)類來(lái)用,也可以把它實(shí)例的類型當(dāng)成接口使用

class Person {
  name: string
  age: number
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
  run(): void { }
}
const foo: Person = {
  name:'foo',
  age:18,
  run(){}
}

實(shí)例的類型是不包括構(gòu)造函數(shù)constructor、靜態(tài)屬性或靜態(tài)方法,因?yàn)樯傻膶?shí)例也不包含這些

class Person {
  name: string
  constructor(name: string) {
    this.name = name
  }
  run(): void { }
  static age: number
  static jump(): void { }
}
const foo: Person = {
  name: 'foo',
  age: 18, //  “age”不在類型“Person”中
  run() { },
  jump(): void { } // jump”不在類型“Person”中
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末韩脏,一起剝皮案震驚了整個(gè)濱河市缩麸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌赡矢,老刑警劉巖杭朱,帶你破解...
    沈念sama閱讀 221,430評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異吹散,居然都是意外死亡弧械,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門空民,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)刃唐,“玉大人,你說(shuō)我怎么就攤上這事界轩』ⅲ” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 167,834評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵耸棒,是天一觀的道長(zhǎng)荒澡。 經(jīng)常有香客問(wèn)我,道長(zhǎng)与殃,這世上最難降的妖魔是什么单山? 我笑而不...
    開(kāi)封第一講書人閱讀 59,543評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮幅疼,結(jié)果婚禮上米奸,老公的妹妹穿的比我還像新娘。我一直安慰自己爽篷,他們只是感情好悴晰,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,547評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般铡溪。 火紅的嫁衣襯著肌膚如雪漂辐。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 52,196評(píng)論 1 308
  • 那天棕硫,我揣著相機(jī)與錄音髓涯,去河邊找鬼。 笑死哈扮,一個(gè)胖子當(dāng)著我的面吹牛纬纪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播滑肉,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼包各,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了靶庙?” 一聲冷哼從身側(cè)響起问畅,我...
    開(kāi)封第一講書人閱讀 39,671評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎六荒,沒(méi)想到半個(gè)月后按声,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,221評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡恬吕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,303評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了须床。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铐料。...
    茶點(diǎn)故事閱讀 40,444評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖豺旬,靈堂內(nèi)的尸體忽然破棺而出钠惩,到底是詐尸還是另有隱情,我是刑警寧澤族阅,帶...
    沈念sama閱讀 36,134評(píng)論 5 350
  • 正文 年R本政府宣布篓跛,位于F島的核電站,受9級(jí)特大地震影響坦刀,放射性物質(zhì)發(fā)生泄漏愧沟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,810評(píng)論 3 333
  • 文/蒙蒙 一鲤遥、第九天 我趴在偏房一處隱蔽的房頂上張望沐寺。 院中可真熱鬧,春花似錦盖奈、人聲如沸混坞。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,285評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)究孕。三九已至啥酱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間厨诸,已是汗流浹背镶殷。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,399評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留泳猬,地道東北人批钠。 一個(gè)月前我還...
    沈念sama閱讀 48,837評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像得封,于是被迫代替她去往敵國(guó)和親埋心。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,455評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容