默认值+TS类型约束提高数据处理成功率

栏目: IT技术 · 发布时间: 4年前

内容简介:我们在处理数据时,常常会遇到某项数据,或者属性是为了更直观的描述问题和解决办法,我们设计了这样一个需求:一般来说,现在的前端组件对表格数据的要求,都是:

我们在处理数据时,常常会遇到某项数据,或者属性是 undefined ,从而引起中断性错误,造成数据处理失败。解决这一问题最直接的办法就是在使用前判断是否 undefined ,但是如果每一个数据使用前都进行判断,非常繁琐,而且容易遗漏。所以这里给大家介绍两个办法

undefined

为了更直观的描述问题和解决办法,我们设计了这样一个需求:

需求:后端响应数据转换成 UI 所需要的数据

一般来说,现在的前端组件对表格数据的要求,都是:

  • 以数组表示数据行,即当前页数据
  • 每行是一个数据对象,其属性对应于表格列
  • 除数据行之外,还包含一些附加信息用于分页,比如

    • 库存数据总数(不是当前数据行的总数)
    • 每页数据呈现条数(一般由前端传给后端,后端返回数据时带出来)
    • 当前页数(一般由前端传给后端,后端返回数据时带出来)

那么,从后端返回的数据可能会是这样(JSON 示例)

{
  "code": 0,
  "message": "查询成功",
  "data": {
    "total": 12345,
    "page": 3,
    "rows": [
      {
        "id": 34,
        "title": "第 34 号数据",
        "stamp": "2020-06-25T20:18:19Z"
      },
      ...
    ]
  }
}

前端呈现如果使用 Layui 的数据表格,它所要求的数据格式是这样的(来自官方文档):

{
  "code": 0,
  "msg": "",
  "count": 1000,
  "data": [{}, {}]
}

远程响应数据结构和表示需要的数据结构之间,对应关系非常明显。

我们用 res 来表示后端 JSON 转成的 JavaScript 对象,那么为 Layui 准备的数据会这样取值:

const tableData = {
    code: res.code,
    msg: res.message,
    count: res.data.total,
    data: res.data.rows
}

这个处理转换非常简单,只是做了一个属性名称的变化。

但是远端返回的数据,可能没有 code ,或者 message ,甚至没有 data ,那么上面的处理结果就可能包括 undefined 。前端组件不希望有这样的值,所以需要添加默认值处理。

利用解构可以赋予默认值的特性

为了演示默认值处理,我们假设,后端返回的数据规范比较灵活,为了节约网络资源,有一个默认值约定:

  • 如果请求正常完成,省略 "code": 0
  • 如果没有特殊消息,省略 "message": ""
  • 如果没有数据,即 "total": 0 的情况,省略 "data": {}
  • 如果当前页没有数据,省略 "rows": []

这种情况下,在进行数据转换时就需要充分考虑到某项数据可能不存在,避免 undefined 错误。

Object.assign() 或者 Spread 运算符可以部分解决这个问题,但是

undefiend

不过我们可以利用解构能够赋予默认值的特性来进行处理。下面这段代码就巧妙地利用了这一特性来避免 undefined 错误。

function getData(res) {
    const { code = 0, message: msg, data = {} } = res;
    const { total: count = 0, rows = [] } = data;

    return {
        code,
        msg,
        count,
        data: rows
    }
}

const tableData = getData(res);

解构确实是可以解决问题,但是如果遗漏或者写错属性,调试起来恐怕不易。比如上面

const { message: msg } = res;

就很容易错写成

const { msg } = res

这是 JS 的硬伤,即使用 ESLint 这样强悍的静态检查 工具 也不能解决。但是如果引入强类型定义,使用 TypeScript 就好办了。

使用 TypeScript 和类型检查转换过程

既然用 TypeScript,就需要为两个数据结构定义对应的类型了。

我们有两个数据结构 restableData ,在 TypeScript 里可以直接把它们定义为 any 类型,这是最简单的操作,但是没什么用 —— 因为 TypeScript 不检查 any 类型。

所以先根据我们之前的约定,把 restableData 的类型定义出来:

interface FetchData<T> {
    total: number;
    page?: number;
    rows?: T[];
}

interface FetchResult<T> {
    code?: number;
    message?: string;
    data?: FetchData<T>;
}

interface LayuiTalbeData<T> {
    code: number;
    msg?: string;
    count: number;
    data: T[];
}

然后要把 res 声明成 FetchResult<T> 类型。这里我们暂时不关心具体每行数据的结构,所以直接用 any 代替好了

const res: FetchResult<any> = await fetch();
// 或者
// const res = await fetch() as FetchResult<any>;

这种情况下,假如我们不小心写错了属性名,比如解构时把源属性 message 错写成了目录属性名 msg ,即 const { msg } = res ,VSCode 是会提示错误的:

默认值+TS类型约束提高数据处理成功率

解决的办法是使用解构重命名:

const { message: msg } = res;

或者,如果我们忘了处理 undefined ,比如忘了给解构的 rows 赋予初始值,那也会得到错误提示,因为

  • 源数据定义中 rows?: T[] 表示它可省略,即可能是 undefined
  • 目标数据定义中 data: T[] 表示它一定不会是 undefined

默认值+TS类型约束提高数据处理成功率

解决的办法是,赋予初始值,使其不可能为 undefined

const { rows: [] } = data;

完整的 TypeScript 代码如下(类型定义参考前面的定义):

function getData<T>(res: FetchResult<T>): LayuiTalbeData<T> {
    const { code = 0, message: msg, data = {} as FetchData<T> } = res;
    const { total: count = 0, rows = [] } = data;

    return {
        code,
        msg,
        count,
        data: rows
    }
}

// 这里 TypeScript 可以推导出 tableData 类型是 LayuiTalbeData<any>
const tableData = getData(res);

使用 Optional Chain 和 Nullish Coalescing

回到 JavaScript,其实还有一个办法可以处理默认值的问题:

const tableData = {
    code: res.code || 0,
    msg: res.message || "",
    count: (res.data && res.data.total) || 0,
    data: (res.data && res.data.rows) || []
}

这也是一个非常常见的办法。这个办法在 TypeScript 中配置类型定义同样可行。只是对多层属性的处理仍然显得有点麻烦。不过 JavaScript 最近引入了“Optional Chain”和“Nullish Coalescing”特性,这个代码可以更简洁:

const tableData = {
    code: res.code ?? 0,
    msg: res.message,
    count: res.data?.total ?? 0,
    data: res.data?.rows ?? []
};

放在 TypeScript 中是这样写的:

const tableData: LayuiTalbeData<any> = {
    code: res.code ?? 0,
    msg: res.message,
    count: res.data?.total ?? 0,
    data: res.data?.rows ?? []
}

上面的 TypeScript 代码中,如果错写了 res.msg 或者忘了加 ?? [] 等,TSC 或者 VSCode 都会有错误提示,以保证你能修正代码。

看,新特性配合 TypeScript 的强类型检查,简直是完美!

小结

我们讲了最简单的数据转换:直接按源数据属性取值。虽然简单,但是有坑,也有处理的方法和技巧:

  • 注意可能出现的 undefinednull ,甚至 NaN 等需要特殊处理的数据
  • 使用解构将属性提取出来,并根据数据结构的需要适当赋予初始值
  • 使用 Optional Chain 和 Nullish Coalescing 简化对可能为 undefined 属性的处理
  • 使用 TypeScript 在开发期检查错误

这里讲的数据处理比较基础,但其中的坑也比较容易被忽略。后面在专栏或订阅号中,我们还是继续探讨更复杂一些的数据处理分析方法和处理技巧,请关注!

默认值+TS类型约束提高数据处理成功率

请关注公众号 边城客栈

看完了先别走,点个 赞 ⇓ 啊, 赞赏 ⇘ 就更好啦!


以上所述就是小编给大家介绍的《默认值+TS类型约束提高数据处理成功率》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

垃圾收集

垃圾收集

琼斯 / 谢之易 / 人民邮电出版社 / 2004-4-1 / 45.00元

书围绕着动态内存自动回收的话题,介绍了垃圾收集机制,详细分析了各种算法和相关技术。 本书共12章。第1章首先介绍计算机存储器管理的演化和自动内存回收的需求,并引入了本书所使用的术语和记法。第2章介绍了3种“经典”的垃圾收集技术:引用计数(reference counting)、标记-清扫(mark-sweep)和节点复制(copying)。 随后的4章更详细地讨论了上述这些垃圾收集方式......一起来看看 《垃圾收集》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具