在傳統的面向對象語言中,接口(Interface)好比協議,它會列出一系列的規則(即對行為進行抽象),再由類來實現這些規則。而TypeScript中的接口更加靈活,除了包含常規的作用之外,它還能擴展其它的類、為對象的類型命名以及約束值的結構等,大大消除了許多潛在的錯誤。
## 一、屬性
  TypeScript中的接口可通過聲明屬性和其類型來限制對象的結構。例如定義一個名為Person的接口,包含一個字符串類型的name屬性和一個數字類型的age屬性,如下所示。
~~~js
interface Person {
name: string;
age: number;
}
~~~
  當聲明一個Person類型的對象時,必須將兩個屬性都定義,并且類型也要與接口中的一致,如下所示。
~~~js
let worker: Person = {
name: "strick",
age: 28
};
~~~
  一旦在worker對象中少定義某個接口中的屬性或多一個在接口中未聲明的屬性,那么就會在編譯階段報錯。注意,TypeScript的類型檢查器不會比對屬性在接口和對象中的定義順序,只要名稱和類型匹配,就能編譯通過。
**1)可選屬性**
  TypeScript允許接口中的屬性定義為可選的,只要在屬性名后跟問號(?),就能變為可選屬性,如下所示。
~~~js
interface Person {
school?: string;
}
~~~
  可選屬性既能預定義可能需要的屬性,也能在捕獲沒有的屬性時給出帶有啟發作用的錯誤提示,例如在創建worker對象時,定義一個schools屬性(如下所示),在編譯時就會報"'schools' does not exist in type 'Person'. Did you mean to write 'school'?"的錯誤。
~~~js
let worker: Person = {
schools: "university"
};
~~~
  由此可知,在對象中定義一個未在接口中聲明的屬性仍然是不允許的。
**2)只讀屬性**
  如果要讓對象的某個屬性只能在創建時被賦值,那么可以將readonly關鍵字作用于相應的接口屬性,使其變為只讀的,如下所示。
~~~js
interface Person {
readonly gender: string;
}
~~~
  由于gender是一個只讀屬性,因此不能在對象初始化后對其進行修改,如下所示。
~~~js
let worker: Person = { //正確
gender: "男"
};
worker.gender = "女"; //錯誤
~~~
**3)任意屬性**
  當接口需要包含任意屬性時,可以通過索引的方式實現,如下所示,用方括號將索引名和索引類型包裹起來。
~~~js
interface Person {
[prop: string]: string;
}
~~~
  在使用Person類型時,可以傳任意多個字符串類型的屬性,如下所示。
~~~js
let worker: Person = {
name: "strick",
gender: "男"
};
~~~
  注意,一旦聲明了任意屬性之后,那么必選屬性和可選屬性都得是其類型的子類型。在下面的示例中,由于可選的age屬性的類型是number,不是string的子類型,因此在編譯時會報錯。
~~~js
interface Person {
name: string; //正確
age?: number; //錯誤
[prop: string]: string;
}
~~~
  TypeScript除了支持字符串類型的索引之外,還支持數字類型的索引,如下所示。
~~~js
interface Person {
[prop: number]: number;
}
~~~
  有一點需要注意,當在接口中同時定義字符串和數字兩種類型的索引時,后者對應的值類型得是前者的子類型。因為這個原因,導致下面的代碼無法在編譯時通過。
~~~js
interface Person {
[prop: string]: string;
[prop: number]: number; //錯誤
}
~~~
  TypeScript之所以如此限制,是因為JavaScript會將數字自動轉換成字符串后再去索引對象,例如用10和“10”兩個值去索引,得到的結果是一樣的,所以兩種索引對應的值類型要保持一致。
## 二、繼承
**1)類繼承接口**
  與C#、Java等面向對象語言一樣,TypeScript中的類也能繼承接口,并且接口中的成員會讓類強制實現。有了接口之后,它的任何更改都有可能導致編譯錯誤,從而就能保證相關代碼的同步。下面通過一個示例來演示類繼承接口,首先創建一個名為Person的接口,包含name屬性和getName()方法,如下所示。
~~~js
interface Person {
name: string;
getName(): string;
}
~~~
  然后再創建一個名為Member的類,通過implements關鍵字繼承Person接口,如下所示。在編譯時,一旦發現類中缺少接口的屬性或方法,就會馬上報錯。
~~~js
class Member implements Person {
name: string = "strick";
getName() {
return this.name;
}
}
~~~
  類能繼承多個接口,只要在類中實現它的成員,就能編譯成功,如下所示,Member類繼承了Person和Profile兩個接口,限于篇幅原因,在其內部省略了name和getName()兩個成員的實現。
~~~js
interface Profile {
school: string;
}
class Member implements Person, Profile {
school: string = "university";
}
~~~
  注意,類不能實現接口中的所有成員,例如在接口中定義一個構造器,再用一個類通過構造函數來實現這個接口,此時編譯將會失敗,代碼如下所示。
~~~js
interface Person {
new (name: string);
}
class Member implements Person {
constructor(name: string) { }
}
~~~
  類包含靜態和實例兩部分,由于編譯器只會對接口的實例部分進行類型檢查,而constructor()函數屬于類的靜態部分,因此會被忽略,從而導致無法在類中找到匹配的成員來實現接口。
  如果要實現接口中的構造器,那么有兩種方式可供選擇。第一種是參數回調,如下代碼所示,Member類不再直接繼承Person接口,而是作為參數傳遞給createPerson()函數,并且其第一個參數被聲明為Person類型。
~~~js
class Member {
constructor(name: string) { }
}
function createPerson(ctor: Person, name: string) {
return new ctor(name);
}
createPerson(Member, "strick");
~~~
  第二種是類表達式,如下代碼所示,將Man變量聲明為Person類型,并把Member類賦給它。
~~~js
let Man: Person = class Member {
constructor(name: string) { }
}
~~~
**2)接口繼承接口**
  接口之間也可相互繼承,這樣既能更細粒度的分割接口,也能最大化的重用代碼。與類不同的是,只需將其它的接口成員復制過來,而不必實現它們。在下面的示例中,Square接口通過extends關鍵字繼承了Shape接口。
~~~js
interface Shape {
background: string;
}
interface Square extends Shape {
width: number;
}
~~~
  一個接口還可以繼承多個其它接口,創建出一個合成接口,如下所示,extends后面跟了Shape和Border兩個接口。
~~~js
interface Border {
color: string;
}
interface Ellipse extends Shape, Border {
angle: string;
}
~~~
**3)接口繼承類**
  當接口繼承一個類時,它會繼承類的所有成員(包括私有和受保護的成員),但不會去實現它們。以下面的TextBox接口為例,它繼承了Control類。
~~~js
class Control {
private width: number;
protected height: number;
}
interface TextBox extends Control {
type: string;
}
class Tel implements TextBox {
type: string = "tel";
}
~~~
  上例中的Tel類直接繼承了TextBox接口,雖然實現了接口中的type屬性,但仍然會報“Type 'Tel' is missing the following properties from type 'TextBox': width, height”的錯誤。因為Button接口繼承的width和height兩個屬性也需要實現。為了避免出現這些錯誤,可以通過Control的子類來實現TextBox接口,如下所示。
~~~js
class Password extends Control implements TextBox {
type: string = "password";
}
~~~
*****
> 原文出處:
[博客園-TypeScript躬行記](https://www.cnblogs.com/strick/category/1561745.html)
[知乎專欄-TypeScript躬行記](https://zhuanlan.zhihu.com/pwts2019)
已建立一個微信前端交流群,如要進群,請先加微信號freedom20180706或掃描下面的二維碼,請求中需注明“看云加群”,在通過請求后就會把你拉進來。還搜集整理了一套[面試資料](https://github.com/pwstrick/daily),歡迎瀏覽。

推薦一款前端監控腳本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不僅能監控前端的錯誤、通信、打印等行為,還能計算各類性能參數,包括 FMP、LCP、FP 等。
- ES6
- 1、let和const
- 2、擴展運算符和剩余參數
- 3、解構
- 4、模板字面量
- 5、對象字面量的擴展
- 6、Symbol
- 7、代碼模塊化
- 8、數字
- 9、字符串
- 10、正則表達式
- 11、對象
- 12、數組
- 13、類型化數組
- 14、函數
- 15、箭頭函數和尾調用優化
- 16、Set
- 17、Map
- 18、迭代器
- 19、生成器
- 20、類
- 21、類的繼承
- 22、Promise
- 23、Promise的靜態方法和應用
- 24、代理和反射
- HTML
- 1、SVG
- 2、WebRTC基礎實踐
- 3、WebRTC視頻通話
- 4、Web音視頻基礎
- CSS進階
- 1、CSS基礎拾遺
- 2、偽類和偽元素
- 3、CSS屬性拾遺
- 4、浮動形狀
- 5、漸變
- 6、濾鏡
- 7、合成
- 8、裁剪和遮罩
- 9、網格布局
- 10、CSS方法論
- 11、管理后臺響應式改造
- React
- 1、函數式編程
- 2、JSX
- 3、組件
- 4、生命周期
- 5、React和DOM
- 6、事件
- 7、表單
- 8、樣式
- 9、組件通信
- 10、高階組件
- 11、Redux基礎
- 12、Redux中間件
- 13、React Router
- 14、測試框架
- 15、React Hooks
- 16、React源碼分析
- 利器
- 1、npm
- 2、Babel
- 3、webpack基礎
- 4、webpack進階
- 5、Git
- 6、Fiddler
- 7、自制腳手架
- 8、VSCode插件研發
- 9、WebView中的頁面調試方法
- Vue.js
- 1、數據綁定
- 2、指令
- 3、樣式和表單
- 4、組件
- 5、組件通信
- 6、內容分發
- 7、渲染函數和JSX
- 8、Vue Router
- 9、Vuex
- TypeScript
- 1、數據類型
- 2、接口
- 3、類
- 4、泛型
- 5、類型兼容性
- 6、高級類型
- 7、命名空間
- 8、裝飾器
- Node.js
- 1、Buffer、流和EventEmitter
- 2、文件系統和網絡
- 3、命令行工具
- 4、自建前端監控系統
- 5、定時任務的調試
- 6、自制短鏈系統
- 7、定時任務的進化史
- 8、通用接口
- 9、微前端實踐
- 10、接口日志查詢
- 11、E2E測試
- 12、BFF
- 13、MySQL歸檔
- 14、壓力測試
- 15、活動規則引擎
- 16、活動配置化
- 17、UmiJS版本升級
- 18、半吊子的可視化搭建系統
- 19、KOA源碼分析(上)
- 20、KOA源碼分析(下)
- 21、花10分鐘入門Node.js
- 22、Node環境升級日志
- 23、Worker threads
- 24、低代碼
- 25、Web自動化測試
- 26、接口攔截和頁面回放實驗
- 27、接口管理
- 28、Cypress自動化測試實踐
- 29、基于Electron的開播助手
- Node.js精進
- 1、模塊化
- 2、異步編程
- 3、流
- 4、事件觸發器
- 5、HTTP
- 6、文件
- 7、日志
- 8、錯誤處理
- 9、性能監控(上)
- 10、性能監控(下)
- 11、Socket.IO
- 12、ElasticSearch
- 監控系統
- 1、SDK
- 2、存儲和分析
- 3、性能監控
- 4、內存泄漏
- 5、小程序
- 6、較長的白屏時間
- 7、頁面奔潰
- 8、shin-monitor源碼分析
- 前端性能精進
- 1、優化方法論之測量
- 2、優化方法論之分析
- 3、瀏覽器之圖像
- 4、瀏覽器之呈現
- 5、瀏覽器之JavaScript
- 6、網絡
- 7、構建
- 前端體驗優化
- 1、概述
- 2、基建
- 3、后端
- 4、數據
- 5、后臺
- Web優化
- 1、CSS優化
- 2、JavaScript優化
- 3、圖像和網絡
- 4、用戶體驗和工具
- 5、網站優化
- 6、優化閉環實踐
- 數據結構與算法
- 1、鏈表
- 2、棧、隊列、散列表和位運算
- 3、二叉樹
- 4、二分查找
- 5、回溯算法
- 6、貪心算法
- 7、分治算法
- 8、動態規劃
- 程序員之路
- 大學
- 2011年
- 2012年
- 2013年
- 2014年
- 項目反思
- 前端基礎學習分享
- 2015年
- 再一次項目反思
- 然并卵
- PC網站CSS分享
- 2016年
- 制造自己的榫卯
- PrimusUI
- 2017年
- 工匠精神
- 2018年
- 2019年
- 前端學習之路分享
- 2020年
- 2021年
- 2022年
- 2023年
- 2024年
- 日志
- 2020