文章目錄
- 接口概念
- 接口和類之間有何關系? 可以使用接口來約束類
- 接口繼承接口
- 接口還可以繼承類
- 接口為什么可以繼承類
- 內層原因:接口為什么可以繼承類
- 用得出的結論解釋最初的demo
- 接口繼承類的一些限制
接口概念
接口(Interfaces)可以用于對「對象的形狀(Shape)」進行描述
interface Box {height: number;color: string;[propName: string]: number | string
}let box1: Box = {height: 888,color: '#f60',weight: '999'
}
接口和類之間有何關系? 可以使用接口來約束類
實現(implements)是面向對象中的一個重要概念
一般來講,一個類只能繼承自另一個類,有時候不同類之間可以有一些共有的特性,這時候就可以把特性提取成接口(interfaces),用 implements 關鍵字來實現。這個特性大大提高了面向對象的靈活性
舉例來說: 門是一個類,防盜門是門的子類。如果防盜門有一個報警器的功能,我們可以簡單的給防盜門添加一個報警方法。這時候如果有另一個類,車,也有報警器的功能,就可以考慮把報警器提取出來,作為一個接口,防盜門和車都去實現它
interface Alarm {alert(): void;
}
class Door {}
class SecurityDoor extends Door implements Alarm {alert(): void {console.log('SecurityDoor alert')}
}class Car implements Alarm {alert(): void {console.log('Car alert')}
}
一個類還可以實現多個接口
interface Alarm {alert(): void;
}
interface Light {lightOn(): void;lightOff(): void;
}class Vehicle implements Alarm, Light {alert(): void {console.log('Vehicle alert')}lightOn(): void {console.log('車燈開') }lightOff(): void {console.log('車燈關')}
}
// Vehicle 實現了 Alarm、Light接口,既能報警又可以開關燈
接口繼承接口
接口和接口之間可以是繼承關系
interface Alarm {alert(): void;
}
interface LightableAlarm extends Alarm {sayHi(): void;sayNo(): void;
}let kf: LightableAlarm = {sayHi():void {},sayNo():void {}
}
這樣才正確,LightableAlarm 繼承了Alarm ,就要有Alarm 接口定義的方法
interface Alarm {alert(): void;
}
interface LightableAlarm extends Alarm {sayHi(): void;sayNo(): void;
}
let kf: LightableAlarm = {sayHi():void {},sayNo():void {},alert(): void {}
}
接口還可以繼承類
常見的面向對象的語言中,接口是不能繼承類的,但是在ts中是可以的
class Point {x: number;y: number;constructor(x: number, y: number) {this.x = xthis.y = y}
}
interface Point3d extends Point {z: number
}
let point3d: Point3d = {x: 1, y: 2, z: 3};
接口為什么可以繼承類
一個類可以當做類來用,也可以當做一個類型來用
class Point {x: number;y: number;constructor(x: number, y: number) {this.x = xthis.y = y}
}// demo: Point 可以當做類來用,也可以當做類型來用// 當做類來用
let p = new Point(1,2)// 當做類型來用
function printPoint(p: Point) {console.log(p)
}
printPoint(new Point(1,2))
內層原因:接口為什么可以繼承類
class Point {x: number;y: number;constructor(x: number, y: number) {this.x = xthis.y = y}
}
interface PointInstanceType {x: number;y: number
}function printPoint1(p: PointInstanceType) {console.log(p)
}
printPoint1(new Point(1,2))
// 上例中我們新聲明的 PointInstanceType 類型,與聲明 class Point 時創建的 Point 類型是等價的
用得出的結論解釋最初的demo
當我們聲明 interface Point3d extends Point
時,Point3d 繼承的實際上是類 Point 的實例的類型
。
換句話說,可以理解為定義了一個接口:PointInstance,Point3d 繼承另一個接口 PointInstance
所以「接口繼承類」和「接口繼承接口」
沒有什么本質的區別。
class Point {x: number;y: number;constructor(x: number, y: number) {this.x = xthis.y = y}
}
// TODO:
// interface PointInstance {
// x: number
// y: number
// }
// interface Point3d extends Point 等價于interface Point3d extends PointInstance
interface Point3d extends Point {z: number
}
let point3d: Point3d = {x: 1, y: 2, z: 3};
接口繼承類的一些限制
如上例,可以看出PointInstance
相比Point
,缺少了constructor方法 ,這是因為聲明 Point 類時創建的 Point 類型是不包含構造函數的。另外,除了構造函數是不包含的,靜態屬性或靜態方法也是不包含的(實例的類型當然不應該包括構造函數、靜態屬性或靜態方法)
換句話說聲明 Point 類時創建的 Point 類型只包含其中的實例屬性和實例方法
;
同樣的 在接口繼承類的時候,也只會繼承它的實例屬性和實例方法
class Point {/** 靜態屬性,坐標系原點 */static origin = new Point(0, 0);/** 靜態方法,計算與原點距離 */static distanceToOrigin(p: Point) {return Math.sqrt(p.x * p.x + p.y * p.y);}/** 實例屬性,x 軸的值 */x: number;/** 實例屬性,y 軸的值 */y: number;/** 構造函數 */constructor(x: number, y: number) {this.x = x;this.y = y;}/** 實例方法,打印此點 */printPoint() {console.log(this.x, this.y);}
}interface PointInstanceType {x: number;y: number;printPoint(): void;
}let p1: Point;
let p2: PointInstanceType;
上例中最后的類型 Point 和類型 PointInstanceType 是等價的。