共计 3134 个字符,预计需要花费 8 分钟才能阅读完成。
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 名都正文成雷同的含意。