当前位置: 首页 > Web前端 > JavaScript

你还在手写TS类型的代码吗?

时间:2023-03-27 11:27:07 JavaScript

作为一个前端开发者,在开发ts项目的时候,最繁琐的工作应该就是手写接口的数据类型和mock数据了,因为如果不做这部分的工作,后面写业务逻辑会很不爽,你要做的就是复制粘贴类似的重复性工作,相当耗时。下面将介绍一种自动生成ts类型和mock数据的方法,帮助同学们摆脱繁琐的工作。下面我们就通过一个例子来让大家了解一下代码生成的基本流程。TS代码生成的基本过程下面我们以下面的ts代码为例,过一遍生成它的基本过程。导出接口TestA{年龄:数字;名称:字符串;其他?:布尔值;朋友:{性别?:1|2;},cats:number[];}第一步:选择数据源我们先来思考一个问题,上面代码写的界面生成需要哪些信息?通过分析,我们首先要知道它有多少个属性,然后要知道哪些属性是必须的。此外,我们还需要知道每个属性的类型、枚举等信息。有一种数据格式可以完美的为我们提供我们所需要的数据,它就是JSONSchema。接触过后端的同学应该都知道JSONSchema,就是对JSON数据的描述。例如,我们定义如下JSON结构:{"age":1,"name":"test","friends":{"sex":1},"cats":[1,2,3],"other":true}我们口头描述这个json:它有五个属性:age,name,friends,cats,other,age属性的类型是number,name属性的类型是string,猫的类型attribute是一个由数字组成的arry,friends属性是一个对象,它有一个sex属性,type是一个数字,other属性的类型是boolean。JSONSchema中的描述如下:{"type":"object","properties":{"age":{"type":"number"},"name":{"type":"string"},"cats":{"type":"array","items":{"type":"number"}},"friends":{"type":"object","properties":{"sex":{“类型”:“数字”},“必需”:[“e”]}},“其他”:{“类型”:“布尔”,},“必需”:[“a”,“b”]}}可以看出,JSONSchema可以完美地以编程方式实现我们的口头描述。这个例子比较简单。JSONSchema的描述能力远不止于此,比如枚举、数组的最大长度、数字的最大最小值等。有必要吗?我们常用的属性都可以准确描述,所以也常用于验证用户输入的场景。第二步:选择代码生成工具。看到这个标题,相信大部分同学都已经知道答案了。没错,就是TSAST和TSCompilerAPI。后者可以生成或修改TSAST,也可以输出编译后的文档。下面我们就来看看如何使用TSCompilerAPI生成抽象语法树,并编译成上面提到的代码。对应的TS编译器代码如下:factory.createInterfaceDeclaration(undefined,[factory.createModifier(ts.SyntaxKind.ExportKeyword)],factory.createIdentifier("TestA"),undefined,undefined,[factory.createPropertySignature(undefined,factory.createIdentifier(“年龄”),未定义,factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)),factory.createPropertySignature(未定义,factory.createIdentifier(“名称”),未定义,factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)),工厂。createPropertySignature(未定义,factory.createIdentifier(“其他”),factory.createToken(ts.SyntaxKind.QuestionToken),factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword)),factory.createPropertySignature(未定义,factory.createIdentifier(“朋友”),未定义,factory.createTypeLiteralNode([factory.createPropertySignature(未定义,factory.createIdentifier(“性别”),factory.createToken(ts.SyntaxKind.QuestionToken),factory.createUnionTypeNode([factory.createLiteralTypeNode(factory.createNumericLiteral(“1”)),factory.createLiteralTypeNode(factory.createNumericLiteral("2"))]))])),factory.createPropertySignature(undefined,factory.createIdentifier("cats"),undefined,factory.createArrayTypeNode(factory.createKeywordTypeNode(ts.SyntaxKind).NumberKeyword)))])乍一看生成这种简单类型的代码很复杂,但是如果把这些方法封装起来,代码会简单很多,而且已经有一些比较成熟的第三方库,比如Tsts-morph等CompilerApi只有英文文档,使用起来比较复杂,而且我们不确定需要调用哪个函数来生成不同类型的代码,但是我们可以去TSASTView中查询,可以生成相应的TS代码根据你输入的TS代码Abstractsyntaxtree和Compiler代码,以上代码由TSASTView提供。factory.createInterfaceDeclaration方法将生成一个接口节点。生成之后,我们需要调用一个方法将生成的界面打印出来,输出到一个字符串文件中。参考代码如下://ast传递代码//需要将上面的factory.createInterfaceDeclaration生成的节点传递给exportconstgenCode=(node:ts.Node,fileName:string)=>{constprinter=ts.createPrinter();constresultFile=ts.createSourceFile(fileName,'',ts.ScriptTarget.Latest,false,ts.ScriptKind.TS);constresult=printer.printNode(ts.EmitHint.Unspecified,node,resultFile);返回结果;};第三步:美化输出代码这一步美化代码我们应该很熟悉了,相信大家都在编译器中安装了Prettier,这是每个前端项目必备的工具。它不仅可以直接格式化我们正在写的文件,还可以格式化我们手动传入的字符串代码。话不多说,我们上代码:import*asprettierfrom'prettier';//默认prettier配置constdefaultPrettierOptions={singleQuote:true,trailingComma:'all',printWidth:120,tabWidth:2,proseWrap:'always',endOfLine:'lf',bracketSpacing:false,arrowFunctionParentheses:'avoid',overrides:[{files:'.prettierrc',选项:{parser:'json',},},{files:'document.ejs',options:{parser:'html',},},],};//格式化美化文件类型prettierFileType=(content:string)=>[string,boolean];exportconstprettierFile:prettierFileType=(content:string)=>{letresult=content;让hasError=false;try{result=prettier.format(content,{parser:'typescript',...defaultPrettierOptions});}catch(error){hasError=true;}返回[结果,hasError];};第四步:将生成的代码写入我们的文件这一步比较简单,直接通过nodefs提供的Api生成文件即可,代码如下://创建目录exportconstmkdir=(dir:string)=>{if(!fs.existsSync(dir)){mkdir(path.dirname(dir));fs.mkdirSync(目录);}};//写入文件exportconstwriteFile=(folderPath:string,fileName:string,content:string)=>{constfilePath=path.join(folderPath,fileName);mkdir(路径.目录名(文件路径));const[prettierContent,hasError]=prettierFile(content);fs.writeFileSync(filePath,prettierContent,{encoding:'utf8',});返回hasError;};前后端协同上述过程还缺少一个重要的步骤:数据源JSONSchema由谁提供?这就需要前后端协同。目前后端已经有非常成熟的生成JSONSchema的工具,比如Swagger、YAPI等接入Swagger的后端项目可以向前端提供swagger.json文件,文件内容包括所有接口的详细数据,包括JSONSchema数据。YAPI与Swagger不同。它是API的集中管理平台。我们可以通过其提供的接口获取所有API的详细数据,类似于swagger.json提供的内容,YAPI平台支持导入或生成swagger.json。如果有接口管理平台,并制定相关规范,前后端协同效率会大大提高,沟通成本会降低,前端也可以做一些工程相关的工作基于管理平台的效率。攻坚克难以上步骤只是简单介绍了生成ts类型代码的思路。这种思路下还有一些难点需要解决,例如:我们在实际开发中需要注解,但是TSCompilerAPI无法生成注解:这个问题我们可以通过在code字符串后的相应地方手动插入注解来解决产生。实际业务的类型可能非常复杂,嵌套层次很深:我们可以通过递归函数来解决这个问题。已经生成的类型代码的API发生了变化,或者新的API应该和原来生成的API放在一个文件中怎么办,这种情况怎么处理?TSComPilerAPI可以读取源文件,甚至可以读取已经存在的文件,我们可以读取源文件,然后使用CompilerAPI修改其抽象语法树,实现修改或添加类型的功能。前后端协同问题:这个需要leader来解决。小结经过上述四个步骤,我们了解了生成代码的基本流程,而且每个步骤的实现都不是固定的,大家可以自行选择:在数据源选择的问题上,我们也可以选择原始的JSONSchema的使用json数据作为数据源,但是生成的类型不是那么准确。这里有一个非常有用的网站:JSON2TS。我们还可以使用一些常用的模板引擎来生成代码生成工具,比如Nunjucks、EJS等,它们不仅可以生成HTML,还可以生成任意格式的文件,并且可以返回生成的字符串。代码美化推荐使用prettier。对于前端来说,输出文件最好的方式就是Node。本文仅提供工程化生成TS类型、Mock数据等简单可复现代码的思路。实施之后,可以减少一部分劳动密集型的工作内容,让我们更专注于业务逻辑开发。参考TSCompilerAPIPrettier