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名都正文成雷同的含意。