TypeScript-接口的使用

前言

眾所周知伪冰,在傳統(tǒng)的JavaScript中是沒有接口的概念的巩踏,所謂的接口,其實就是描述集合屬性的類型的一個特殊的虛擬結(jié)構(gòu)音榜。這也是開發(fā)一個大型項目所必須的語言特性稚叹,像Java动雹、C#這樣強類型語言槽卫,接口已經(jīng)使用得非常廣泛。于是胰蝠,在TypeScrip中也引入了接口的概念歼培。

一震蒋、接口的基本使用

基與我們前面介紹的對象的類型的聲明,可以定義一個函數(shù)的參數(shù)是包含特定屬性的對象:

function doSomeThing(params: {name: string}):void {
  console.log(params); 
}

console.log(doSomeThing({name: '馬松松'}));

// { name: '馬松松' }

我們也可以使用接口的方式實現(xiàn)上面的例子:

interface person {
  name: string
}
function doSomeThing(params: person):void {
  console.log(params); 
}

console.log(doSomeThing({name: '馬松松'}));

// { name: '馬松松' }

兩者是等效的躲庄,使用接口的好處是可以將參數(shù)類型的配置抽離到一個單獨的文件查剖,這樣使得項目更容易維護。

二噪窘、接口中使用可選參數(shù)

為了增強接口的靈活性和延展性笋庄,TypeScript允許定義為接口類型的變量可以選擇性匹配。

interface SquireParams {
  width?: number,
  height?: number
}

function squireResult(params: SquireParams):any {
  let result: any;
  if (params.width) {
    result = params.width * params.width;
  }
  if (params.height) {
    result = params.height * params.height;
  }
  if (params.width && params.height) {
    result = params.width * params.height;
  }

  return result;
}

console.log(squireResult({height: 5}));
// 25

console.log(squireResult({width: 5}));
// 25

console.log(squireResult({width: 5,height: 5}));
// 25

當(dāng)然倔监,也可以和必選參數(shù)結(jié)合使用:

interface SquireParams {
  width?: number,
  height?: number,
  label: string
}

function squireResult(params: SquireParams):any {
  let result: any;
  if (params.width) {
    result = params.label +  params.width * params.width;
  }
  if (params.height) {
    result = params.label + params.height * params.height;
  }
  if (params.width && params.height) {
    result = params.label + params.width * params.height;
  }

  return result;
}

console.log(squireResult({label: '計算結(jié)果為:', height: 5}));
// 計算結(jié)果為:25

三直砂、接口中使用 只讀屬性

同時,在JavaScript中浩习,沒有關(guān)鍵字標(biāo)識只讀屬性静暂。我們可以通過Object.defineProperty屬性設(shè)置攔截,在TypeScript中明確提出了只讀屬性的關(guān)鍵字谱秽。

可以這樣使用:

interface readonlyType {
  readonly x: number,
  readonly y: number
}

let readonlyObj: readonlyType = {x: 10, y: 10}
readonlyObj.x = 13;

//Cannot assign to 'x' because it is a read-only property 

只允許初始化的時候洽蛀,給xy分配number的值。

對于數(shù)組弯院,TypeScript也提供了ReadonlyArray<T>這樣的泛型只讀數(shù)組,刪除了該命名數(shù)組的操作數(shù)組的所有方法泪掀。

const arr: ReadonlyArray<number> = [1,2,3];

當(dāng)你想往該數(shù)組推入一個數(shù)字時听绳,會引發(fā)錯誤:

arr.push()
// Property 'push' does not exist on type 'readonly number[]'

??對于const和readonly的使用的場景:

TypeScript的官方推薦是:變量使用const,而屬性使用readonly异赫。

四椅挣、Excess Property Checks

這個是解決原生的JavaScript的行為和TypeScript行為不一致的方案,思考這樣一個例子:

interface SquareConfig {
  color ?: string,
  width ?: number
}

function createSquare(config: SquareConfig): { color: string; area: number } {
  return { color: config.color || "red", area: config.width ? config.width * config.width : 20 };
}

我們定義了一個SquareConfig接口塔拳,然后作為函數(shù)的入?yún)㈩愋褪笾ぃ缓笪覀冞@樣使用這個函數(shù):

let mySquare = createSquare({ colour: "red", width: 100 });

這里TypeScript會給出錯誤提示類型不匹配,但是按照我們之前說的可選參數(shù)的的例子靠抑,這里的color并不是必須的量九,因為這里故意將color拼成了colour,TypeScript對以字面量方式定義對象的方式進行了特殊的類型檢查處理颂碧,而在原生的JavaScript中是靜默忽略的荠列,為了避免出現(xiàn)這種情況,下面是幾種更好的規(guī)避這種錯誤的方式:

1.使用as 強制推斷類型

let mySquare = createSquare({colour: "red", width: 100} as SquareConfig);

2.不使用字面量的方式

let paramsSquare = {colour: "red", width: 100};
let mySecondSquare = createSquare(paramsSquare);

3.加一個額外的動態(tài)屬性

interface SquareConfig {
  color ?: string,
  width ?: number,
  [propName: string]: any;
}

let myThirdSquare = createSquare({colour: "red", width: 100});

當(dāng)你想用傳字面量的方式傳入?yún)?shù)载城,為了規(guī)避不必要的錯誤肌似,使用上面的幾種方式就行。

五诉瓦、在接口中定義 函數(shù)的參數(shù)類型和返回值類型

1.基本使用:

首先定義一個函數(shù)的接口川队,我們定義了參數(shù)的類型和返回值的類型

interface baseFunc {
  (firstName: string, lastName: string): string
}

然后這樣使用這個接口:

let myFunc: baseFunc  = function (firstName: string, lastName: string) {
  return firstName + lastName;
}

2.函數(shù)的入?yún)⒉恍枰?/p>

let mySecondFunc: baseFunc  = function (fName: string, lName: string) {
  return fName + lName;
}

3.當(dāng)你指定了函數(shù)簽名的類型 函數(shù)的入?yún)⒑头祷刂狄膊恍枰该黝愋土ο福愋拖到y(tǒng)會自動根據(jù)傳入的參數(shù)推斷類型

let myThirdFunc: baseFunc  = function (fName, lName) {
  return fName + lName;
}

4.但是如果你沒有指定類型 但是返回了和接口返回類型不一致 類型檢查不會通過

let myLastFunc: baseFunc  = function (fName, lName) {
  let result =  fName + lName;

  return 11;
}

六、接口中 定義數(shù)組和對象的索引類型

1.基本使用:

interface objectType {
  [index: string]: string;
}

在對象中這樣使用這個接口

let myObj: objectType = {name: '馬松松', age: "18"}; 

可以看到固额,我們定義的索引是string眠蚂,屬性值也是string,所以這樣定義是合理的。

但是如果將age的屬性定義為number類型对雪,就不符合我們接口的定義:

let myObj: objectType = {name: '馬松松', age: 18}; // 這樣是不符合接口的定義的

在數(shù)組中需要這樣使用定義接口河狐,數(shù)組的索引都是number類型的:

interface arrayType {
  [index: number]: string;
}

然后,你可以這樣使用這個接口:

let myArr: arrayType = ["馬松松","18"];

2.注意字符串索引和直接指定類型的方式一起使用的時候,字符串索引類型的優(yōu)先級更高,所以直接指明屬性的類型 需要保持和字符串索引一樣.

interface numberDictionary {
  [index: string]: number,
  length: number,
  // name: string // 這里使用string會報錯瑟捣,以為你字符串索引返回的類型是number
  name: number, // 這樣是可以的
}

3.那你確實想定義不同類型的屬性 可以這樣做

interface secondNumberDictionary {
  [index: string]: number | string,
  length: number,
  name: string // 這樣是可以的
}

4.也可以結(jié)合 readonly 定義只讀屬性

interface thirdNumberDistionary {
  readonly [index: string]: string
}

// 此時當(dāng)你想設(shè)置thirdNumberDistionary的屬性的時候就會報錯
let myThirdNumberDictionary: thirdNumberDistionary = {name: '馬松松'};
// myThirdNumberDictionary.name = "宋志露"; // 不可設(shè)置

七馋艺、類和接口的關(guān)系

其他語言中使用接口做頻繁的操作的就是,用類實現(xiàn)一個接口迈套,從而使得類和接口締結(jié)某種強制的聯(lián)系捐祠。

1.基本使用:

我們首先定義一個日期接口:

interface BaseClock {
  currentTime: string
}

使用implements關(guān)鍵詞締結(jié)類和接口的契約關(guān)系:

class MyClock implements BaseClock {
  currentTime: ""
  constructor(h: number, m: number) {

  }
}

締結(jié)的契約關(guān)系為:MyClock類中必須有類型為stringcurrentTime變量。

2.也可以締結(jié)類中的方法的契約

先定義接口:

interface SecondBaseClock {
  getCurrentTime(t: string): void
}

使用implements締結(jié)契約:

class MySecondClock implements SecondBaseClock {
  getCurrentTime(t: string) {
    this.currentTime = t;
  }
}

締結(jié)的契約關(guān)系為:MySecondClock類中需要有一個getCurrentTime方法桑李,且需要一個類型為string的入?yún)Ⅴ庵瑳]有返回值。

3.在締結(jié)類和接口的契約關(guān)系時 注意new關(guān)鍵詞

當(dāng)使用new關(guān)鍵詞實例化類時贵白,TypeScript類型檢查器不會檢查靜態(tài)類的構(gòu)造器方法是否滿足締約率拒,而是在你使用new關(guān)鍵詞的時候判斷是否滿足。

比如我們定義一個構(gòu)造函數(shù)的的接口:

interface C {
  new (hour: number, min: number)
}

然后使用implements締結(jié)契約:

class Clock implements C {
  constructor(h: number, m: number) {}
}

我們會得到報錯信息:

Class 'Clock' incorrectly implements interface 'C'.Type 'Clock' provides no match for the signature 'new (hour: number, min: number): any'

我們締結(jié)契約的類實際上是滿足了構(gòu)造函數(shù)的接口的禁荒,但是由于TypeScript類型檢查不會直接檢查類中構(gòu)造函數(shù)是否滿足契約猬膨,所以這里會報錯。

所以正確的使用方式是將締結(jié)契約的類賦值給別的變量呛伴,這樣類型檢查系統(tǒng)就會進行類型檢查:

interface ClockInterface {
  tick(): void
}
const Clock: ClockConstructor = class Clock implements ClockInterface {
  constructor(h: number, m: number) {}
  tick() {

  }
}

這里注意這樣的區(qū)別就好了勃痴。

八、接口中 使用繼承

1.基本使用

我們首先定義一個Square接口:

interface Square {
  width: number,
  height: number
}

然后這樣使用:

let square = {} as Square;
square.width = 100;
square.height = 100;

為了使接口可以更靈活的構(gòu)建更復(fù)雜的數(shù)據(jù)結(jié)構(gòu),這里使用到了extends關(guān)鍵字:

interface baseSquare {
  width: number,
}

interface Square extends baseSquare {
  height: number
}
let square = {} as Square;
square.width = 100;
square.height = 100;

2.一個接口可以繼承多個接口

interface baseFirstSquare {
  width: number,
}
interface baseSecondSquare {
  width: number,
}

然后我們可以同時繼承這樣兩個接口:

class MySquare implements baseFirstSarare,baseSecondSquare {
  color: string
}

九热康、接口中使用 混合類型

基于JavaScript語言的豐富性和靈活性沛申,TypeScript允許使用混合類型

比如定義一個定時器接口:

interface Counter {
  (start: number): string;
  interval: number;
  reset(): void;
}

然后你這樣使用:

function getCounter(): Counter {
  let counter = function (start: number) {} as Counter;
  counter.interval = 123;
  counter.reset = function () {};
  return counter;
}

let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;

這里使用as推斷了類型,就獲取了一個對象姐军,這里個人有點不理解铁材。

九、接口繼承

1.當(dāng)繼承的類是public時奕锌,可以直接實現(xiàn)這個接口

class Control {
  public state: any
}

interface SelectableControl extends Control {
  select(): void
}

這樣使用:

let select: SelectableControl = {
  state: 22,
  select() {}
}

2.當(dāng)繼承的類private或者protected時 繼承的接口只能通過被繼承類子類去實現(xiàn)衫贬,不能直接實現(xiàn)

class SecondControl {
  private state: any
}

interface SecondSelectableControl extends SecondControl {
  select(): void
}

只能是被繼承類的子類去實現(xiàn)該接口,因為只有被繼承類的子類才能訪問私有屬性:

class MySecondSelectableControl extends SecondControl implements SecondSelectableControl {
  select() {

  }
}

然后你這樣使用:

let s = new MySecondSelectableControl();

總結(jié):接口的使用歇攻,其實也是引進了強類型語言的相關(guān)的概念固惯,理解接口概念的同時,同時也能增強前端開發(fā)者對強類型語言和弱類型語言的特性的理解缴守。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末葬毫,一起剝皮案震驚了整個濱河市镇辉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌贴捡,老刑警劉巖忽肛,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異烂斋,居然都是意外死亡屹逛,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進店門汛骂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來罕模,“玉大人,你說我怎么就攤上這事帘瞭∈缯疲” “怎么了?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵蝶念,是天一觀的道長抛腕。 經(jīng)常有香客問我,道長媒殉,這世上最難降的妖魔是什么担敌? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮廷蓉,結(jié)果婚禮上全封,老公的妹妹穿的比我還像新娘。我一直安慰自己苦酱,他們只是感情好售貌,可當(dāng)我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布给猾。 她就那樣靜靜地躺著疫萤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪敢伸。 梳的紋絲不亂的頭發(fā)上扯饶,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天,我揣著相機與錄音池颈,去河邊找鬼尾序。 笑死,一個胖子當(dāng)著我的面吹牛躯砰,可吹牛的內(nèi)容都是我干的每币。 我是一名探鬼主播,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼琢歇,長吁一口氣:“原來是場噩夢啊……” “哼兰怠!你這毒婦竟也來了梦鉴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤揭保,失蹤者是張志新(化名)和其女友劉穎肥橙,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秸侣,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡存筏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了味榛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片椭坚。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖励负,靈堂內(nèi)的尸體忽然破棺而出藕溅,到底是詐尸還是另有隱情,我是刑警寧澤继榆,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布巾表,位于F島的核電站,受9級特大地震影響略吨,放射性物質(zhì)發(fā)生泄漏集币。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一翠忠、第九天 我趴在偏房一處隱蔽的房頂上張望鞠苟。 院中可真熱鬧,春花似錦秽之、人聲如沸当娱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽跨细。三九已至,卻和暖如春河质,著一層夾襖步出監(jiān)牢的瞬間冀惭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工掀鹅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留散休,地道東北人。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓乐尊,卻偏偏與公主長得像戚丸,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子扔嵌,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,440評論 2 359

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