10、TS接口配合类使用(进阶篇)

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情

接口用于约束类、对象、函数, 是一个类型契约。前面的文章将了接口约束其它类型, 今天了解接口如何约束类。

接口怎么配合类

结合一个例子来描述接口配合类的好处:

假如有这样一种场景,在马戏团中有很多动物它们都具有一下特征:

  • 年龄
  • 种类
  • 昵称

这里就可以创建一个 动物类 抽象类描述上面的特征:

//动物类
abstract class Animal{
    abstract age:number //年龄
    abstract name:string //名字
    abstract nickname:string //昵称
}
复制代码

这里创建 狮子、猴子、狗类 继承至 动物类

//动物类
abstract class Animal {
    abstract age: number //年龄
    abstract type: string //名字
    abstract nickname: string //昵称
}

class Lion extends Animal {
    type: string = '食肉目猫科狮亚属的大型动物'
    constructor(public age: number, public nickname: string) {
        super()
    }
}

class Monkey extends Animal {
    type: string = '哺乳纲灵长目'
    constructor(public age: number, public nickname: string) {
        super()
    }
}

class Dog extends Animal {
    type: string = '犬科哺乳动物'
    constructor(public age: number, public nickname: string) {
        super()
    }
}
复制代码

然后分别创建这些动物

const lion = new Lion(2, '小白')
const monkey = new Monkey(1, '复航')
const dog = new Dog(1, '旺财')
复制代码

现在马戏团对这些动物进行专业的训练

  • 小白学会了跳火圈
  • 复航学会了说脱口秀
  • 旺财学会了做数学题

这就代表着它们三有了动物不具备的特殊 技能(skill), 所以上面创建 狮子、猴子、狗的类需要加上这些动物不具备的能力,改造一下 class 的实现。

class Lion extends Animal {
    type: string = '食肉目猫科狮亚属的大型动物'
    constructor(public age: number, public nickname: string) {
        super()
    }
    //跳火圈能力
    tiàoHuǒQuān() {
        console.log(`${this.nickname}会跳火圈`)
    }
}

class Monkey extends Animal {
    type: string = '哺乳纲灵长目'
    constructor(public age: number, public nickname: string) {
        super()
    }
    //脱口秀能力
    tuokouxiu() {
        console.log(`${this.nickname}会脱口秀`)
    }
}

class Dog extends Animal {
    type: string = '犬科哺乳动物'
    constructor(public age: number, public nickname: string) {
        super()
    }
    //数学题能力
    shuxueti() {
        console.log(`${this.nickname}会数学题`)
    }
}
复制代码

此时马戏团开始上台表演了:

  • 请会跳火圈的动物表演

那么问题来了, 代码该怎么写? 也许会这样写:

const animals: Animal[] = [lion, monkey, dog]
animals.forEach(a => {
    if ((a as Lion).tiàoHuǒQuān) {
        (a as Lion).tiàoHuǒQuān()
    }
})
复制代码

从逻辑上这里判断了某一个动物是否具有 tiàoHuǒQuān 方法,而不是拥有 会跳火圈的能力 从逻辑上这个判断就已经是错误的, 只要逻辑错误代码就必然存在隐患。

解决方法: 类配合接口

定义接口

//跳火圈
interface IJump {
    tiàoHuǒQuān(): void
}

//脱口秀
interface ITalkShow {
    tuokouxiu(): void
}

//数学题
interface IMath {
    shuxueti(): void
}
复制代码

只要类使用了该接口那么类就一定要实现接口的成员方法

接口配合类使用

implements 接口名

class Lion extends Animal implements IJump {
    type: string = '食肉目猫科狮亚属的大型动物'
    constructor(public age: number, public nickname: string) {
        super()
    }
    //跳火圈能力
    tiàoHuǒQuān() {
        console.log(`${this.nickname}会跳火圈`)
    }
}
复制代码

Lion类有 IJump进行强约束, 所以Lion类必须实现Ijump的成员tiàoHuǒQuān否在在ts中则报错。

现在回归到马戏团表演的问题

此时马戏团开始上台表演了:

  • 请会跳火圈的动物表演

那么问题来了, 代码该怎么写?

animals.forEach(a => {
    if(a instanceof IJump){

    }
})
复制代码

上面的写法通过关键字instanceof判断 a 是否有 IJump的所有成员在 java, c## 语言中是允许的, 但在 ts 中则不合法(因为接口不存在编译结果中), 为了更好的书写这个判断这里可以写一个函数(类型保护函数)专门用于判断 a 是不是某种类型。

类型保护函数

//判断是否游跳火圈的能力
function hasIjump(animal: object): animal is IJump {
    return (animal as IJump).tiàoHuǒQuān !== undefined
}
复制代码

有了这个类型保护函数的辅助, 接下来的一切将会变得很自然

animals.forEach(a => {
    if (hasIjump(a)) { //这里的语义化就是判断 a 动物是否拥有跳火圈的能力
        a.tiàoHuǒQuān()
    }
})
复制代码

在后续的训练中 也学会了跳火圈的能力, 那么对代码轻微了修改

class Dog extends Animal implements IJump, IMath {
    type: string = '犬科哺乳动物'
    constructor(public age: number, public nickname: string) {
        super()
    }
    //跳火圈能力
    tiàoHuǒQuān(): void {
        console.log(`${this.nickname}会跳火圈`)
    }
    //数学题能力
    shuxueti() {
        console.log(`${this.nickname}会数学题`)
    }
复制代码

现在 拥有了跳火圈的能力, 但并不会印象其他代码的执行, 这就是接口配合类的好处,更容易扩展。

接口可以继承类

class A {
    a1: string = ''
    a2: string = ''
    a3: string = ''
}

class B {
    b1: number = 0
    b2: number = 0
    b3: number = 0
}

interface C extends A, B {

}

const c:C = {
    a1: '',
    a2: '',
    a3: '',
    b1: 0,
    b2: 0,
    b3: 0
}
复制代码

总结

类型保护函数: 通过调用该函数、会触发TS的累心保护、该函数必须返回 boolean

接口和类型别名的最大区别: 接口可以被类显示、而类型别名不可以。

猜你喜欢

转载自juejin.im/post/7086818933616934942