類實(shí)現(xiàn)接口
類類型接口是類遵循的某種約束和規(guī)則尺借。
類可以通過關(guān)鍵字 implements
實(shí)現(xiàn)接口。
interface IPerson {
name: string;
eat(): void
}
class Person implements IPerson {
// 如果構(gòu)造函數(shù)參數(shù)中顯式設(shè)置屬性的訪問控制符為public怎憋,則會(huì)默認(rèn)初始化該屬性
constructor(public name: string) {}
eat(): void {
console.log( `${this.name} 吃飯` )
}
}
const p1 = new Person('mike')
console.log(p1)
此外通過上文可知屿衅,一個(gè)類只能繼承另一個(gè)類,但有時(shí)不同類之間卻有一些共同的特性擅腰,這時(shí)可以把共有特性提取成接口蟋恬,使用類去實(shí)現(xiàn)。
列舉網(wǎng)上的一個(gè)案例:門是一個(gè)類趁冈,防盜門是門的子類歼争。如果防盜門有一個(gè)報(bào)警器的功能,我們可以給防盜門添加一個(gè)報(bào)警方法渗勘。這時(shí)候如果有另一個(gè)類沐绒,車,也有報(bào)警器的功能旺坠,就可以考慮把報(bào)警器提取出來乔遮,作為一個(gè)接口,防盜門和車都去實(shí)現(xiàn)它:
interface Alarm {
alarm(): void
}
class Door { }
class SecurityDoor extends Door implements Alarm {
alarm() {
console.log('SecurityDoor alarm')
}
}
class Car implements Alarm {
alarm() {
console.log('Car alarm')
}
}
一個(gè)類可以同時(shí)實(shí)現(xiàn)多個(gè)接口:
interface IPerson {
name: string;
eat(): void;
}
interface IPersonX {
age: number;
skill(): string;
}
class PersonX implements IPerson, IPersonX {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
eat(): void {
console.log( `${this.name} 吃飯` )
}
skill(): string {
return `${this.name} age is ${this.age} sleep`
}
}
const p2 = new PersonX('nike', 12)
類的靜態(tài)部分和實(shí)例部分
關(guān)于類需要明確的一點(diǎn)是取刃,類有兩部分蹋肮,靜態(tài)部分和實(shí)例部分。
通過以上方式定義的接口只會(huì)檢查類實(shí)例的屬性璧疗,對(duì)于類靜態(tài)屬性的檢查需要單獨(dú)定義接口坯辩。
首先通過下面例子思考一個(gè)問題:
interface PersonInterface {
name: string
age: number
eat(): void
}
const person: PersonInterface = {
name: 'mike',
age: 12,
eat() {
console.log('food')
}
}
class Person implements PersonInterface {
name: string
age: number
constructor() {}
eat() {
console.log('food')
}
}
接口PersonInterface很明顯是一個(gè)對(duì)象類型的接口,為什么類實(shí)現(xiàn)接口時(shí)崩侠,類中存在constructor方法確不報(bào)錯(cuò)漆魔?那么類實(shí)現(xiàn)的接口究竟是對(duì)誰(shuí)的約束?
事實(shí)上類實(shí)現(xiàn)的接口只是對(duì)類實(shí)例出來的對(duì)象的約束却音,只會(huì)對(duì)其實(shí)例屬性進(jìn)行檢查改抡。而TS中constructor方法則屬于類的靜態(tài)方法,即屬于類的靜態(tài)部分僧家,對(duì)靜態(tài)屬性的約束需要額外定義接口雀摘。
TS中類的靜態(tài)部分指的是這個(gè)類本身;
實(shí)例部分指的是類實(shí)例化出來的對(duì)象八拱;
所以對(duì)類約束的接口需要通過以下方式定義:
interface IDogInterface {
new (name: string, age: number)
getName(): void
}
const Dog: IDogInterface = class {
name: string
age: number
constructor(name, age) {
this.name = name
this.age = age
}
static getName() {
console.log('dog')
}
}
再看下面例子:
interface PersonInterface {
name: string
age: number
eat(): void
}
class Person implements PersonInterface {
name: string
age: number
constructor(name, age) {
this.name = name
this.age = age
}
eat() {
console.log('food')
}
}
const person = new Person(33, 'mike')
以上類Person實(shí)現(xiàn)接口PersonInterface阵赠,在類中我們定義了兩個(gè)公有屬性name、age肌稻,并對(duì)其類型進(jìn)行了約束清蚀,注意:只是給類實(shí)例定義了兩個(gè)屬性name、age爹谭。
創(chuàng)建實(shí)例時(shí)枷邪,調(diào)用構(gòu)造方法constructor,該函數(shù)接收的兩個(gè)參數(shù)的類型是實(shí)際上是any類型诺凡,和公有屬性name东揣、age沒有關(guān)系践惑。創(chuàng)建實(shí)例的過程和js是一致的(首先創(chuàng)建一個(gè)對(duì)象,然后初始化該對(duì)象嘶卧,再然后賦值給this尔觉,最后返回this)。
但是如果通過實(shí)例修改name的值芥吟,如果類型不一致則會(huì)報(bào)錯(cuò)侦铜。
person.name = 22 // Type '22' is not assignable to type 'string'
由此可以看出類實(shí)現(xiàn)的接口僅僅是對(duì)類實(shí)例的約束。
所以對(duì)一個(gè)類的實(shí)例部分和靜態(tài)部分的約束需要定義兩個(gè)接口钟鸵,一個(gè)用來檢查靜態(tài)部分钉稍,一個(gè)用來檢查實(shí)例部分。
// PersonConstructor 用來檢查靜態(tài)部分的
interface PersonConstructor {
new (name: string, age: number): PersonInterface // 檢查 constructor 并且創(chuàng)建的類必須是通過PersonInterface接口實(shí)現(xiàn)的
typename: string // 檢查靜態(tài)屬性 typename
logname(): void // 檢查靜態(tài)方法 logname
}
// PersonInterface 用來檢查實(shí)例部分的
interface PersonInterface {
// new (name: string, age: number) // 靜態(tài)方法的檢查也不能寫在這里 否則會(huì)報(bào)錯(cuò)
log(): void // 定義實(shí)例方法 log
}
// 創(chuàng)建一個(gè)類棺耍,需要對(duì)類的靜態(tài)部分(類本身)和實(shí)例部分都進(jìn)行約束
const PersonClass: PersonConstructor = class implements PersonInterface {
name: string
age: number
static typename = 'Person type' // 定義名為 typename 的靜態(tài)屬性
static logname() { // 定義名為 logname 的靜態(tài)方法
console.log(this.typename)
}
constructor(name: string, age: number) { // constructor 也是靜態(tài)方法
this.name = name
this.age = age
}
log() { // log 是實(shí)例方法
console.log(this.name, this.age)
}
}
new (name: string, age: number): PersonInterface
這里的意思是該接口對(duì)類的靜態(tài)部分進(jìn)行檢查贡未,并且我們創(chuàng)建的類必須是通過 PersonInterface
接口實(shí)現(xiàn)的。
如果僅僅是這樣定義的 new (name: string, age: number)
烈掠,那么滿足該接口結(jié)構(gòu)的類所實(shí)現(xiàn)的接口不會(huì)有限制羞秤。是不是有點(diǎn)繞∽蟮校看例子說話:
interface IPersonConstructor<T> {
new (name: string, age: number): T
getName(): void
}
interface IPersonInterfaceX {
eat(): void
}
interface IPersonInterfaceY {
skill(): void
}
const PersonX: IPersonConstructor<IPersonInterfaceX> = class implements IPersonInterfaceX {
static getName() {
console.log('jack')
}
name: string
age: number
constructor(name, age) {
this.name = name
this.age = age
}
eat() {
console.log('food')
}
}
const px = new PersonX('nike', 23)
px.eat()
const PersonY: IPersonConstructor<IPersonInterfaceY> = class implements IPersonInterfaceY {
static getName() {
console.log('jack')
}
name: string
age: number
constructor(name, age) {
this.name = name
this.age = age
}
skill() {
console.log('food')
}
}
const py = new PersonY('jack', 34)
py.skill()
以上得出:滿足接口IPersonConstructor的類,它實(shí)現(xiàn)的類可以實(shí)現(xiàn) IPersonInterfaceX 和 IPersonInterfaceY 接口俐镐。而new (name: string, age: number): PersonInterface
接口結(jié)構(gòu)的類矫限,它所實(shí)現(xiàn)的接口,必須滿足PersonInterface接口佩抹。
記椎鸱纭:靜態(tài)屬性和方法的檢查、實(shí)例屬性和方法的檢查是不同的 Interface
接口繼承類
class Point {
x: number
y: number
}
interface Point3d extends Point {
z: number
}
let point3d: Point3d = {x: 1, y: 2, z: 3}
接口也可以繼承多個(gè)類:
class PointX {
x: number
}
class PointY {
y: number
}
interface Point3d extends PointX, PointY {
z: number
}
let point3d: Point3d = {x: 1, y: 2, z: 3}
接口繼承接口
接口可以繼承一個(gè)或多個(gè)接口棍苹。查看接口繼承以及混合類型TypeScript(二)接口
類繼承
一個(gè)類可以繼承另一個(gè)類无宿,查看TypeScript(三)類