关于前端:JSON对象转换TypeScript声明

3次阅读

共计 3134 个字符,预计需要花费 8 分钟才能阅读完成。

TypeScript 简介

TypeScript(TS)是一种强类型的编程语言,因为在编译时便会发现代码存在的问题,被前端团队大量应用。

TS 使开发人员可能把握各种组件的交互方式,并使代码重构变得更加容易;但对于须要精确定义的类型,在应用时会比拟麻烦。特地是波及数个及数十个后端接口数据的类型定义,会有大量的转换和的工夫老本。通过 VSCode 插件实现一键生成 TS 申明,能够节俭编程过程的开发成本。

Auto-ts-inline-types 简介

为了克服 TS 的变量类型申明过于繁琐的毛病,节约开发成本,举荐应用 VSCode 插件【Auto-ts-inline-types】。它能够通过可视化窗口,模仿一个网络申请。通过接口返回的信息,或者手动批改后的信息,主动生成对应的 TS 申明。

此文章次要介绍插件中 Json 对象生成 TS 申明的转换原理。

转换原理

(一)生成所有的 interface 对象

  1. 首先对须要转换的 Json 对象进行遍历,遍历对象所有属性。当遇到属性 key/value 中的 value 是 object 的状况,则开始递归,以此循环。
  2. 当某个对象遍历后,发现所有属性的 value 都是根本类型,则生成 typeDescription(蕴含 HashId 和 typeObj)。
    (备注:HashId 为引入第三方包 Hash 后,通过将 typeObj 转化为 HashId 而来,通过 HashId 保障了 typeDescription 的唯一性)
// 数据结构示例:const HashId = 'cc1c310866bbc757b533d7650c9e9934b2e47bf9';
const typeObj = {
  name: string;
  version: string;
  description: string;
  main: string;
  author: string;
  license: string;
}
  1. 递归开始由内层向外层开始顺次 return,最初返回的 typeStructure 中蕴含 rootTypeId 和 types(蕴含所有生成的 typeDescription)。

(备注:如下图所示,在生成的 types 数组中蕴含所有的 interface 申明。types 中每个元素的的 HashId 属性用来保护元数据 Json 对象的层级关系。但在 types 中,所有对象都是平级的,因为生成的 interface 都是平级的。在后续的转换过程中,会将 HashId 替换为变量名,以此生成 TS 申明)

  1. 如果发现某个对象的属性中,存在某些 value 为数组类型,则对数组进行遍历递归。

Value 是数组的状况有以下 5 种:

对于数组的 typeDescription 构造体如下图所示。【arrayOfTypes】代表该数组中的所有元素类型;【isUnion】代表是下面的表格中,元素类型的第 5 种状况(多元素类型)。

(二)生成对应 Names 数组

把接口返回的信息转化为 rootTypeId 和 types 组成的对象后,就须要通过 HashId 和 types 的对应关系,找到 HashId 对应的 Key,再将 Key 的首字母变为大写,生成 interface 中的 Name。保护好一份 HashId 和 Name 的 Json 对应关系后,寄存于数组 Names 中,在转换过程的最初进行替换。

  1. 首先判断由 Json 对象转换而来的 TypeStructure 对象的构造,取出 RootTypeId。在 types 数组中找到 RootTypeId 对应的 typeDescription,遍历 typeObj。
  2. 依据 typeObj 的数据结构,通过 arrayOfTypes 来判断是否是数组。
  3. 如果是数组,则生成对应的类型。
  4. 如果是对象,则主动生成对象(蕴含 HashId 与 name),放入 names 数组中。

(三)依据 HashId 映射生成 TS 申明

  1. 通过 HashId 做映射,将 Names 数组中的 name 作为 interface 的名称。
  2. 在 types 数组中,依据 HashId 寻找每个 interface 中的属性。如果存在属性的 value 是 HashId 的,则在 Names 中查找对应的 name。
  3. 将生成的 interface 对象,变换为字符串的模式,蕴含 interface 以及 JSDoc 正文。
/**
 * @description 结构 interface 构造体
 */
function getInterfaceStringFromDescription(_a) {
    var name = _a.name, typeMap = _a.typeMap;
    // 结构 interface 内部结构
    var stringTypeMap = Object.entries(typeMap)
        .map(function (_a) {var key = _a[0], name = _a[1];
            return "" + key +": "+ name +";n";
        })
        .reduce(function (a, b) {return (a += b); }, "");

    // 正文阐明
    var descriptionName = `/**n *@description ${name}n`
    // 属性类型
    var descriptionTypeMap = Object.entries(typeMap)
        .map(function (_a) {var key = _a[0], name = _a[1];
            return "*@param {" + name + "}" + key + "n";
        })
        .reduce(function (a, b) {return (a += b); }, "");
    // Doc 正文
    var descriptionString = descriptionName + descriptionTypeMap + \'*/n\'

    // 结构 interface 构造体
    var interfaceString = "interface" + name + "{n";
    interfaceString += stringTypeMap;
    interfaceString += "}";
    return descriptionString + interfaceString;
}

(四)生成 JSDoc 正文

能够在接口返回信息中,间接通过正文来增加每个字段的含意。

  1. 收集所有的用户手动增加的正文信息,生成正文信息 json 构造体。
  2. 在接口的返回信息中,去掉用户的正文,以使返回的文本信息能够转换为 Json 构造体。
  1. 在转换的后果中,替换掉曾经正文含意的字段,并写入生成的 JSDoc 正文中。
/**
 * 将中文含意写入 JSDoc 正文
 */
const getFinalInterface = (text: string) => {for (let key in commentJson) {
    // commentJson 是所有的字段和中文含意的对应关系
        text = text.replace(key + \'n\', key + \'\' + commentJson[key] + \'n\');
        text = text.replace(
            key + "\'" + \'n\',
            key + "\'" + \'\' + commentJson[key] + \'n\'
        );
    }
    return text;
};

问题总结

  1. 主动生成的 interface 对象的程序为递归的程序,json 对象的最外层 interface(RootType)会被写入生成文件的最上面。目前是手动调整程序,将 interface 名为 RootType 的申明置于最顶层,其余字段依照从上到下的顺序排列。
if (name === \'RootObject\') {
    // 如果是根对象,则增加至数组头部
    nameMap.unshift({id, name});
} else {
    // 如果是其余对象,则在数组前面顺次增加
    nameMap.push({id, name});
}
  1. 如果在接口返回信息中,存在多个雷同 key 名的字段,并且用户手动写了不同的含意。目前 JSDoc 正文会将所有的 key 名都正文成雷同的含意。
正文完
 0