当前位置:网站首页>你还在手写TS类型代码吗
你还在手写TS类型代码吗
2022-06-10 16:58:00 【腾讯新闻前端团队】
身为一个前端开发,在开发ts项目时,最繁琐的工作应该就是手写接口的数据类型和mock数据,因为这部分工作如果不做,后面写业务逻辑难受,做的话全是复制粘贴类似的重复工作,还挺费时间。下文将给大家介绍一个自动生成ts类型和mock数据的方法,帮助同学们从繁琐得工作中解脱出来。
下面我们将通过一个示例,让大家一起了解一下代码生成的基本过程。
TS代码生成基本流程
我们以下面这段ts代码为例,一起过一下生成它的基本流程。
export interface TestA {
age: number;
name: string;
other?: boolean;
friends: {
sex?: 1 | 2;
},
cats: number[];
}第一步:选定数据源
我们先思考一个问题,把上述代码写的interface生成需要哪些信息?
通过分析,我们首先需要知道它一共有几个属性,然后要知道哪些属性是必须的,除此以外还需要知道每个属性的类型、枚举等信息。有一种数据格式可以完美的给我们提供我们所需要的数据,它就是JSON Schema。
接触过后端的同学应该都了解过JSON Schema,它是对JSON数据的描述,举个例子,我们定义了下面这个JSON结构:
{
"age": 1,
"name": "测试",
"friends": {
"sex": 1
},
"cats": [
1,
2,
3
],
"other": true
}我们口头描述下这个json:它有age、name、friends、cats、other5个属性,age属性的类型是number,name属性的类型是string,cats属性的类型是number组成的arry,friends属性是一个object,它有一个sex属性,类型是数字,other属性的类型是boolean。
用JSON Schema的描述如下:
{
"type": "object",
"properties": {
"age": {
"type": "number"
},
"name": {
"type": "string"
},
"cats": {
"type": "array",
"items": {
"type": "number"
}
},
"friends": {
"type": "object",
"properties": {
"sex": {
"type": "number"
},
"required": [
"e"
]
}
},
"other": {
"type": "boolean",
},
"required": [
"a",
"b"
]
}
}可以看出JSON Schema可以完美的程序化实现我们的口头描述,这个例子比较简单,JSON Schema的描述能力远不止于此,比如枚举,数组的最大长度,数字的最大最小值,是否是必须的等我们常用的属性都能精确描述,所以它也常用于用户输入校验的场景。
第二步:选定代码生成工具
看到这个标题,相信大多数同学都已经知道了答案,没错,就是TS AST和TS Compiler API,后者可以生成或者修改TS AST,也可以输出编译后的文件。我们来看一下如何使用TS Compiler API生成抽象语法树并且编译成上文中提的代码。
对应的TS Compiler代码如下:
factory.createInterfaceDeclaration(
undefined,
[factory.createModifier(ts.SyntaxKind.ExportKeyword)],
factory.createIdentifier("TestA"),
undefined,
undefined,
[
factory.createPropertySignature(
undefined,
factory.createIdentifier("age"),
undefined,
factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)
),
factory.createPropertySignature(
undefined,
factory.createIdentifier("name"),
undefined,
factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
),
factory.createPropertySignature(
undefined,
factory.createIdentifier("other"),
factory.createToken(ts.SyntaxKind.QuestionToken),
factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword)
),
factory.createPropertySignature(
undefined,
factory.createIdentifier("friends"),
undefined,
factory.createTypeLiteralNode([factory.createPropertySignature(
undefined,
factory.createIdentifier("sex"),
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))
)
]
)乍一看生成这段简单类型的代码非常复杂,但是仔细一看如果这些方法经过封装,代码会简洁不少,而且目前已经有一些比较成熟的第三方库库,比如ts-morph等。
Ts Compiler Api只有英文文档,而且使用复杂,而且生成不同类型的代码需要调用哪个函数我们不好确定,但我们可以去TS AST View查询,它能根据你输入的TS代码生成对应的抽象语法树和Compiler代码,上述代码就是TS AST View提供的。
factory.createInterfaceDeclaration方法会生成一个interface节点,生成之后,我们还需要调用一个方法将生成的interface打印出来,输出成字符串文件,参考代码如下:
// ast转代码
// 需要将上文factory.createInterfaceDeclaration生成的节点传入
export const genCode = (node: ts.Node, fileName: string) => {
const printer = ts.createPrinter();
const resultFile = ts.createSourceFile(fileName, '', ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
const result = printer.printNode(
ts.EmitHint.Unspecified,
node,
resultFile
);
return result;
};第三步:美化输出的代码
美化代码这一步我们应该很熟悉了,相信我们编译器中都装有Prettier,每个前端项目必备的工具,它不仅可以直接格式化我们正在编写的文件,也可以格式化我们手动传入的字符串代码,话不多说,上代码:
import * as prettier from 'prettier';
// 默认的prettier配置
const defaultPrettierOptions = {
singleQuote: true,
trailingComma: 'all',
printWidth: 120,
tabWidth: 2,
proseWrap: 'always',
endOfLine: 'lf',
bracketSpacing: false,
arrowFunctionParentheses: 'avoid',
overrides: [
{
files: '.prettierrc',
options: {
parser: 'json',
},
},
{
files: 'document.ejs',
options: {
parser: 'html',
},
},
],
};
// 格式化美化文件
type prettierFileType = (content:string) => [string, boolean];
export const prettierFile: prettierFileType = (content:string) => {
let result = content;
let hasError = false;
try {
result = prettier.format(content, {
parser: 'typescript',
...defaultPrettierOptions
});
}
catch (error) {
hasError = true;
}
return [result, hasError];
};第四步:将生成的代码写入我们的文件
这一步比较简单,直接是有node提供的fs Api生成文件即可,代码如下:
// 创建目录
export const mkdir = (dir:string) => {
if (!fs.existsSync(dir)) {
mkdir(path.dirname(dir));
fs.mkdirSync(dir);
}
};
// 写文件
export const writeFile = (folderPath:string, fileName:string, content:string) => {
const filePath = path.join(folderPath, fileName);
mkdir(path.dirname(filePath));
const [prettierContent, hasError] = prettierFile(content);
fs.writeFileSync(filePath, prettierContent, {
encoding: 'utf8',
});
return hasError;
};前后端的协同
上面的流程还缺少重要的一步:数据源JSON Schema谁提供?
这就需要前后端的协同,目前后端已经有了很成熟的生成JSON Schema的工具,比如Swagger,YAPI等。
接入Swagger的后端系项目都能给前端提供swagger.json文件,文件的内容就包括所有接口的详细数据,包括JSON Schema数据。
YAPI和Swagger不同,它是API的集中管理平台,在它上面管理的api我们都可以通过它提供的接口获取的所有api的详细数据,和swagger.json提供的内容大同小异,而且YAPI平台支持导入或者生成swagger.json。
如果有了接口管理平台和制定了相关规范,前后端的协作效率会提升很多,减少沟通成本,而且前端也可以基于管理平台做一些工程效能相关的工作。
难点攻克
上述步骤只是简单的介绍了一下生成ts类型代码的一个思路,这思路下还有有一些难点需要解决的,比如:
- 实际开发中我们需要注释,但TS Compiler API不能生成注释:这个问题我们可以通过再代码的string生成之后然后在对应的地方手动插入注释的方式解决。
- 实际业务的类型可能非常复杂,嵌套层次很深:这个问题我们可以通过递归函数来解决。
- 已经生成的类型代码,如果API有改动,应该怎么办,或者新增的API要和原来生成的放的一个文件下,这种情况怎么处理?TS ComPiler API是可以读取源文件的,就是已经存在的文件也是可以读取的,我们可以读取源文件然后再利用Compiler API修改它的抽象语法树实现修改或者追加类型的功能。
- 前后端的协同问题:这个就需要找leader解决了。
总结
经过上面提到的四个步骤,我们了解了生成代码的基本流程,而且每一步的实现方案不是固定的,可以自行选择:
- 在数据源选择的问题上,我们除了JSON Schema还可以选择原始的json数据当作数据源,只是生成的类型不是那么精准,在这推荐一个很好用的网站:JSON2TS。
- 代码生成工具我们也可以用常用的一些模板引擎来生成,比如Nunjucks,EJS等,它们不仅可以生成HTML,也可以生成任何格式的文件,并且能够返回生成的字符串。
- 代码美化这步还是推荐使用prettier。
- 对于前端来说,目前最好的输出文件的方式就是Node了。
本文只提供了一种工程化生成TS类型、Mock数据等简单可复制代码的思路,实现后能减少一部分劳动密集型的工作内容,让我们更专注于业务逻辑开发。
参考文献
边栏推荐
- 蓝桥杯_挑选子串_组合数学_乘法原理_ / 尺取法
- [BSP video tutorial] BSP video tutorial issue 17: single chip microcomputer bootloader topic, startup, jump configuration and various usage of debugging and downloading (2022-06-10)
- 单片机底层通信协议① —— 同步和异步、并行和串行、全双工和半双工以及单工、电平信号和差分信号
- Overseas data centers need to be prepared for unpredictable disasters
- Force buckle 20 Valid parentheses
- IIS安装 部署网站
- mmcv之Config类介绍
- Only three steps are needed to learn how to use low code thingjs to connect with Sen data Dix data
- PCA主成分分析教程(origin分析&绘制,无须R语言)
- 2022G1工业锅炉司炉考题及在线模拟考试
猜你喜欢

仅需三步学会使用低代码ThingJS与森数据DIX数据对接

matplotlib plt. Specific usage of text() - labeling points in a drawing

5年后,你将如何融入20万亿美元的「项目经济」

2022年上海市安全员C证操作证考试题库模拟考试平台操作

B站不想成为“良心版爱优腾”

matplotlib plt.text()的具体用法——画图时给图中的点加标签

自定义视图:图形与图像的处理(一):使用简单图片

Swift 3pThread tool Promise Pipeline Master/Slave Serial Thread confinement Serial queue

mmdetection之dataloader构建

丢失的遗传力--Missing heritability
随机推荐
well! One new star, please look over | elder martial brother and elder martial sister say
Swift 3pThread tool Promise Pipeline Master/Slave Serial Thread confinement Serial queue
matplotlib plt.text()的具体用法——画图时给图中的点加标签
成立1年便成独角兽,腾讯滴滴做「靠山」,今年新晋的独角兽不简单
为什么宇宙会将最大速度限制在光速
蓝桥杯_糊涂人寄信_递归
Summary of vim common commands
Xinsi technology performed well in the Gartner application security test key capability report 2022 and won the highest score among the five common use cases
为什么 0.1+0.2=0.30000000000000004
2022年T电梯修理考试题模拟考试题库及在线模拟考试
mmdetection之model构建
Fabric. JSON for JS compact output
SVN中的回退操作
protoc-gen-go-grpc‘不是内部或外部命令,也不是可运行的程序 或批处理文件
Force buckle 20 Valid parentheses
See how advanced technology changes human life
Swift 3pThread tool Promise Pipeline Master/Slave Serial Thread confinement Serial queue
Snabbdom virtual DOM (I)
传统企业在进行信息化升级的过程中,如何做好信息化顶层设计
2022年上海市安全员C证操作证考试题库模拟考试平台操作