TypeScript简介
TypeScript(TS)是一种强类型的编程语言,因为在编译时便会发现代码存在的问题,被前端团队大量应用。
TS使开发人员可能把握各种组件的交互方式,并使代码重构变得更加容易;但对于须要精确定义的类型,在应用时会比拟麻烦。特地是波及数个及数十个后端接口数据的类型定义,会有大量的转换和的工夫老本。通过VSCode插件实现一键生成TS申明,能够节俭编程过程的开发成本。
Auto-ts-inline-types简介
为了克服TS的变量类型申明过于繁琐的毛病,节约开发成本,举荐应用VSCode插件【Auto-ts-inline-types】。它能够通过可视化窗口,模仿一个网络申请。通过接口返回的信息,或者手动批改后的信息,主动生成对应的TS申明。
此文章次要介绍插件中Json对象生成TS申明的转换原理。
转换原理
(一)生成所有的interface对象
- 首先对须要转换的Json对象进行遍历,遍历对象所有属性。当遇到属性key/value中的value是object的状况,则开始递归,以此循环。
- 当某个对象遍历后,发现所有属性的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;}
- 递归开始由内层向外层开始顺次return,最初返回的typeStructure中蕴含rootTypeId和types(蕴含所有生成的typeDescription)。
(备注:如下图所示,在生成的types数组中蕴含所有的interface申明。types中每个元素的的HashId属性用来保护元数据Json对象的层级关系。但在types中,所有对象都是平级的,因为生成的interface都是平级的。在后续的转换过程中,会将HashId替换为变量名,以此生成TS申明)
- 如果发现某个对象的属性中,存在某些value为数组类型,则对数组进行遍历递归。
Value是数组的状况有以下5种:
对于数组的typeDescription构造体如下图所示。【arrayOfTypes】代表该数组中的所有元素类型;【isUnion】代表是下面的表格中,元素类型的第5种状况(多元素类型)。
(二)生成对应Names数组
把接口返回的信息转化为rootTypeId和types组成的对象后,就须要通过HashId和types的对应关系,找到HashId对应的Key,再将Key的首字母变为大写,生成interface中的Name。保护好一份HashId和Name的Json对应关系后,寄存于数组Names中,在转换过程的最初进行替换。
- 首先判断由Json对象转换而来的TypeStructure对象的构造,取出RootTypeId。在types数组中找到RootTypeId对应的typeDescription,遍历typeObj。
- 依据typeObj的数据结构,通过arrayOfTypes来判断是否是数组。
- 如果是数组,则生成对应的类型。
- 如果是对象,则主动生成对象(蕴含HashId与name),放入names数组中。
(三)依据HashId映射生成TS申明
- 通过HashId做映射,将Names数组中的name作为interface的名称。
- 在types数组中,依据HashId寻找每个interface中的属性。如果存在属性的value是HashId的,则在Names中查找对应的name。
- 将生成的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正文
能够在接口返回信息中,间接通过正文来增加每个字段的含意。
- 收集所有的用户手动增加的正文信息,生成正文信息json构造体。
- 在接口的返回信息中,去掉用户的正文,以使返回的文本信息能够转换为Json构造体。
- 在转换的后果中,替换掉曾经正文含意的字段,并写入生成的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;};
问题总结
- 主动生成的interface对象的程序为递归的程序,json对象的最外层interface(RootType)会被写入生成文件的最上面。目前是手动调整程序,将interface名为RootType的申明置于最顶层,其余字段依照从上到下的顺序排列。
if (name === \'RootObject\') { // 如果是根对象,则增加至数组头部 nameMap.unshift({ id, name });} else { // 如果是其余对象,则在数组前面顺次增加 nameMap.push({ id, name });}
- 如果在接口返回信息中,存在多个雷同key名的字段,并且用户手动写了不同的含意。目前JSDoc正文会将所有的key名都正文成雷同的含意。