剖析Typescript接口(Interface)

接口(Interface)

Typescript最核心的原则之一,就是对值所具有的结构进行类型检查,就是我们常说的 “鸭式辩型法”或者是“结构性子类型机构”;
Typescript中的interface的作用就是为这些类型命名,为编码者或者第三方代码提供一个定义好的契约;

接口的使用

我们假设一个场景,在编写js的函数时,又一个getUserId的方法,我们要通过这个方法直接回去userId这个属性;可以这么写:

const geyUserId = (user) => user.userId; //参数“user”隐式具有“any”类型

在ts中发现“参数“user”隐式具有“any”类型”这个提示。也就是说我们必须要用一种类型描述这个user参数,但是这个类型又不是我们常见的基本类型;这个时候就用到了接口(interface):

interface User {
    userId: string,
    name: string
}
const getUserAge= (user: User) => user.userId;

通过这个接口User描述了参数user的类型,可以找到对应的属性存在并且兼容即可;

可选属性

我们在开深入扩展一下这场景,当这个user属性可能不存在age这个属性怎么办?常见场景就是在处理表单中,age这个字段是可选,我该用接口怎么描述呢?

interface User {
    userId: string,
    name: string,
    age?: number
}
const getUserAge= (user: User) => user.age;

鼠标移上去可以看到一句提示语:(property) User.age?: number | undefined;也就是说如果age不存在的话接口中属性的类型可以是undefined;存在值的时候是number类型;

只读属性

再来扩展一下我们的场景,表单中有些属性是默认值,不允许修改,我们考虑一下怎么设置呢?
其实很简单就是利用readonly我们可以把一个属性变成只读性质;用来禁止用户对他的操作;

interface User {
    userId: string,
    name: string,
    age?: number,
    readonly nation: string
}
const setUserNation = (param: User) => param.nation = '02'; 

我们在编写的过程中发现代码出现了错误警告 “无法分配到 “nation” ,因为它是只读属性。”

函数类型

我们再来想一下,如果这个接口中需要一个函数,来作为他的默认函数,怎么办?
user中属性映射一个函数很简单;

interface Say {
    (word: string): string
}
interface User {
    userId: string,
    name: string,
    age?: number,
    readonly nation: string,
    say: Say
}

const sayUserInfo = (user: User) => {
    user.say = (word:string) => user.name;
}

属性检查

假设我们又一个Config接口,我们想配合函数实现计算面积的功能:

/**
 * @description 计算面积
 */
interface Setting {
    width?: number
}

const getArea = (config: Setting): { area: number } {
    let square = 100;
    if (config.width) {
        square = config.width * config.width;
    }
    return { area: square };
}

let mySquare = getArea({ widths: 5 }); //类型“{ widths: number; }”的参数不能赋给类型“Setting”的参数。
                                      //对象文字只能指定已知的属性,但“widths”中不存在类型“Setting”。是否要写入 width ?

这里我故意把width写成widths结果出现了参数传递经过属性检查的情况,这种情况我们有没有主流的解决方案?

官方推荐三种方案:

  • 类型断言
let mySquare = getArea({ widths: 5 }) as Setting
  • 添加字符串索引签名
interface Setting {
    width?: number,
    [propName:string]:any
}

这种我们发现可以传入任意数量的属性,而且无所谓是什么类型;

  • 字面量赋值给另外一个变量
interface Setting {
    width?: number
}
let options: any = { widths: 5 }
const getArea = (config: Setting): { area: number } {
    let square = 100;
    if (config.width) {
        square = config.width * config.width;
    }
    return { area: square };
}

let mySquare = getArea(options)

本质上是转化为any类型,这种方法不是非常情况,不建议使用,过于暴力;

可索引类型

我在开发中遇到一种场景,user属性中某一个属性是个集合,不确定多少成员;这种怎么使用,我们先看一下数据;

{
    data: [
        {
            name: 'xiaozhang',
            age: 18,
            isMale: false,
            say: function(){},
            email: {
                NetEase: '[email protected]',
                qq: '[email protected]',
            }
        },
        {
            name: 'xiaoming',
            age: 16,
            isMale: true,
            say: function () { },
            email: {
                NetEase: '[email protected]',
                qq: '[email protected]',
                sina: '[email protected]',
            }
        }
    ]
}

我们发现email的属性成员不确定,属性有一些共性特征,key全部都是string类型,value也是string类型;个性是成员数量不等;
我们面对这种场景就可以使用可索引的类型,原理就是利用其索引签名,描述对象索引的类型及相应索引返回值的类型;

interface Say {
    (word: string): string
}
interface Email {
    [name: string]: string
}

interface User {
    userId: string,
    name: string,
    age?: number,
    readonly nation: string,
    say: Say,
    email: Email
}

接口的继承

我们在面向对象中常见的对象继承,使用接口也是可以的;

interface Say {
    (word: string): string
}
interface Email {
    [name: string]: string
}

interface User {
    userId: string,
    name: string,
    age?: number,
    readonly nation: string,
    say: Say,
    email: Email
}


interface VipUser extends User {
    vipcard:string,
    vipSay:Say
}

同理也可以继承多个接口,使用“,”分开;

总结

以上我们了解了接口的一些基本用法,还有一些简单场景的结合;总之,接口(interface)是一个非常强大的代码类型定义描述,欢迎探讨。

你可能感兴趣的:(interface)