Typescript入门之接口

1,180 阅读5分钟

作为一个小职员,工作多,没提成,人家休假我加班。房贷没还清,保险自己买。不过好在我不用交停车费,因为我根本买不起车。2020年小编决定好好的学习Typescript,提升个人能力,努力赚小钱钱,争取买到一辆自行车(呸,要啥自行车!!!!)。这是小编写的第四篇Typescript学习的文章。

Typescript入门之基本类型(一)

Typescript入门之基本类型(二)

Typescript入门之数组与元组

Typescript入门之接口

最近同事王七比较牛逼,天天开一个劳斯莱斯来上班,就他那点工资,都不够油钱,可是人家就这么牛逼。后来打听了一下,原来是他女朋友铁锤仙子送的。据说铁锤仙子过生日的那天,他表现的特别好,铁锤仙子一高兴,就送他一辆车,也是幸福啊。人家女朋友是真有钱,俗话说,女大三,抱金砖,他女朋友足足报了15块金砖。

接口定义

这不,有一天王铁锤仙子给王七说,你看你哥哥弟弟(你懂得哈)们也多,我年龄大了,有时候记不清楚谁是谁,你给我做一个男朋友管理系统吧,这是一百万,你先花着,不够再说啊。 王七一想,男朋友管理系统,先定义一个男朋友的对象吧。

// 男朋友接口
interface IBoyFriend {
   id: string,
   name: string,
   age: number,
   height: number,
   weight: number,
   // 就不注释了
   time: number,
   duration: number,
   photo: string
}
// 定义一个男朋友
const boyFriend: IBoyFriend = {
   id: string,
   name: '王七',
   age: 25,
   height: 180,
   weight: 75,
   time: 2,
   duration: 15,
   photo: 'http://xxxxxxxxxx.jpg'
}

// 新增方法
function add(boyFriend: IBoyFriend): void {
   // do something
}

// 抹杀方法,肯定不是删除那么简单了
function obliterate(boyFriend: IBoyFriend): void{
   // do someting
}

// 查询方法
function list(): IBoyFriend[] {
   return [boyFriend]
}

可选属性与只读属性

这两天铁锤仙子有点兴奋,新认识了一个外国小哥,体验贼好了。可是想把小哥录入系统时候,发现没有地方指定小哥的国籍的。就让王七给加一下王七脑子一转,三下五除二就写了下面的代码

interface IBodyFriend{
    name: string,
    // 其他省略
    ...,
    // 国籍
    nationality?: string
}

// 定义一个男朋友
const boyFriend: IBoyFriend = {
    id: '1'
   name: '王七',
   age: 25,
   height: 180,
   weight: 75,
   time: 2,
   duration: 15,
   photo: 'http://xxxxxxxxxx.jpg',
   nationality: '中国'
}

function add(boyFriend: IBoyFriend): void {
  // 如果不选,默认为中国
   if(boyFriend.nationality === undefiend) {
       boyFriend.nationality = '中国'
   }
   // do someting
}

对于可选属性,即使不赋值也不会报错。

const obj = Object.freeze({score: 0})

王七功能做完了,屁颠屁颠去给铁锤表功,没想到又挨了一顿骂,原来是铁锤想查找其他人信息,确一直显示王七的信息,功劳没有拿到,又有活干了,王七检查了一下代码,发现了这样一段代码

// 王七把自己的id写死成1了
if(user.id = '1') {
    // 给自己美言几句话
} else {
    // 偷偷的贬低别人
}

原来是把===写成了=,那应该如何避免这类问题了,王七想到了只读属性

interface IBoyFriend{
    readonly id: string
}

对于只读属性,只能在初始化对象赋值的时候赋值,不能在中间过程中修改值

多余属性检查

有时候虽然接口定义了一系列属性,但实际上外部传入的属性可能会比定义的属性要多,就拿王七定义的男朋友接口来说,有name,age啥的,但是铁锤仙子的闺蜜给她推送了一批新的数据

const data: IBoyFriend[] = [{
   id: '2'
   name: '王八',
   age: 25,
   height: 180,
   weight: 75,
   time: 2,
   duration: 15,
   photo: 'http://xxxxxxxxxx.jpg',
   nationality: '坦桑尼亚',
   // 多余的属性
   // 报错 Object literal may only specify known properties, and 'birthday' does not exist in type 'IBoyFriend'.
   birthday: 'xxxx-xx-xx',
   sex: '女'
}]

这时候因为IBoyFriend里面没有定义多余的属性,所以会报错,对于这些不属于接口的数据,可以通过多余属性检查来避免报错

interface IBodyFriend{
    name: string,
    // 其他省略
    ...,
    // 额外的属性检查
    [propName: string]: any
}

函数类型

接口可以描述对象拥有的各种各样的外形,除了带属性的普通对象外,还可以定义函数的外形。 下面就是王七定义的根据国籍查询列表的函数

// 定义函数类型
interface ISearchByNation {
    // 冒号左侧为参数类型,右侧为返回值类型
    (nation: string): IBoyFriend[]
}

const search: ISearchByNation = (nation) => {
    return []
}

有时候在定义函数的时候,还需要绑定一些额外的属性,比如我们希望一个按钮事件,如果300毫秒内重复点击,则不触发

interface IClick {
    (e: Event): void,
    // 额外的属性
    timer?: number
}

const click: IClick = (e) => {
    if (click.timer) {
        return
    }
    // do something
    click.timer = setTimeout(() => {
        click.timer = undefined
    },300)
}

类类型

铁锤仙子最近带着王七参加了几次车展,买了几辆兰博基尼,劳斯莱斯,林肯,奔驰啥的。每天开着豪车去外面潇洒玩游戏也感觉挺不错的。

// 定义一辆车的类型
interface ICar {
    // 品牌
    brand: string,
    // 型号
    modelNumber: string,
    // 开车
    drive(): void,
    // 玩游戏
    play(): void
}


/**
 * 对于类类型来说,接口只会定义类的公共部分的内容,比如`brand`,`drive`,并不会管其他的属性或方法,如下面的`other`,`otherMethod`
 */
class Car implements ICar {
    brand = ''
    modelNumber = ''
    // 其他信息
    other = ''
    otherMethod() { }

    
    constructor(brand: string, modelNumber: string) {
        this.brand = brand
        this.modelNumber = modelNumber
    }
    drive() {
        // 开车
    }
    play() {
        // 玩游戏
    }
}

const car = new Car('奔驰', '梅赛德斯-AMG C 63')
// 开车
car.drive()
// 玩游戏
car.play()

土豪出门会开一辆车吗,不可能,都是要出动车队的


/**
 * 当你操作类和接口的时候,类是具有两个类型的:静态部分的类型和实例的类型
 * 当定义一个类接口的时候,其实只定义了类的实例部分, 如上 ICar
 * 当需要操作类的时候,需要定义类的静态部分,如下
 */
interface ICarContructor{
    new(brand:string, modelNumber: string): any
}
// 定义车队
function motorcade(CarCont: ICarContructor, brand:string, modelNumber:string): void {
    const car = new CarCont(brand, modelNumber)
    // 开车
    car.drive()
    // 玩游戏
    car.play()
} 

motorcade(Car, '奔驰', '梅赛德斯-AMG C 63')
motorcade(Car, '兰博基尼', 'Huracan')

接口继承

/**************王七出差去美国和建国同志聊天去了*******************/

和类一样,接口也可以相互继承,这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。

interface IHuman{
    sex: string,
    name: string,
    height: number,
    weight: number
}

interface IWoman extends IHuman {
    // 撒娇
    coquetry(): void,
    // 卖萌
    actingCute(): void
}

interface IMan extends IHuman{
    makeMoney(): void
}