基于JS实现将JSON数据转换为TypeScript类型声明的工具
作者:forrest酱 发布时间:2023-06-12 16:20:44
在TypeScript 项目中,我们经常需要使用声明一系列的ts类型。然而,手动写的效率实在太低,编写一个自动生成ts类型的工具可以解放生产力。 实现一个工具将 JSON 数据转换为 TypeScript 类型定义,从而让 TypeScript 项目更高效的开发。
一、实现的功能
将 JSON 数据转换为 TypeScript 类型定义。
支持嵌套的复杂类型,如数组和对象。
支持自定义类型名称和命名空间。
支持将转换后的 TypeScript 类型定义保存为文件。
二、工具使用方法
已经发布到npm:my-json-to-ts - npm (npmjs.com)
运行效果如下动图:
安装工具:
npm install -g my-json-to-ts
运行工具:
my-json-to-ts input.json output.ts
其中 input.json
是要转换的 JSON 文件路径,output.ts
是转换后的 TypeScript 文件路径。
--name 类型名称 # 指定转换后的类型名称,默认为 JsonType
--namespace 命名空间 # 指定转换后的命名空间,默认为无
--no-file # 不将转换后的 TypeScript 类型定义保存为文件
三、实现思路
读取输入的 JSON 文件,解析成 JSON 对象。
遍历 JSON 对象,根据不同的类型生成对应的 TypeScript 类型定义字符串。
如果指定了类型名称和命名空间,则在生成的 TypeScript 类型定义字符串前面添加对应的声明。
如果指定了保存文件,则将生成的 TypeScript 类型定义字符串写入文件。
四、使用示例
以下是将JSON 数据和转换后的 TypeScript 类型定义示例:
简单的JSON 数据
{
"name": "John",
"age": 30,
"address": {
"city": "New York",
"state": "NY"
},
"hobbies": [
"reading",
"traveling"
]
}
输出对应简单的类型定义
interface JsonType {
name: string;
age: number;
address: {
city: string;
state: string;
};
hobbies: string[];
}
复杂的JSON 数据
{
"name": "John",
"age": 30,
"address": {
"city": "New York",
"state": "NY",
"postalCode": 10001
},
"friends": [
{
"name": "Jane",
"age": 28,
"address": {
"city": "Los Angeles",
"state": "CA"
}
},
{
"name": "Bob",
"age": 35,
"address": {
"city": "Chicago",
"state": "IL"
}
}
],
"hobbies": [
"reading",
"traveling",
{
"name": "swimming",
"location": "pool"
}
]
}
输出对应复杂类型定义
interface JsonType {
name: string;
age: number;
address: {
city: string;
state: string;
postalCode: number;
};
friends: {
name: string;
age: number;
address: {
city: string;
state: string;
};
}[];
hobbies: (string | {
name: string;
location: string;
})[];
}
五、具体实现代码
首先引入两个 Node.js 模块:fs-extra
和 commander
。fs-extra
是一个简化了 Node.js 文件系统模块的封装,而 commander
是一个命令行工具的库,可以方便地解析命令行参数。
接下来定义一个函数 jsonToTs
,用于将 JSON 数据转换为 TypeScript 类型定义字符串。该函数采用递归的方式遍历 JSON 数据,生成对应的 TypeScript 类型定义。如果 JSON 数据是数组,则递归处理其中的每个元素;如果是对象,则递归处理其中的每个属性。最终,该函数返回一个 TypeScript 类型定义字符串。
然后定义了两个异步函数,readJson
和 writeTs
,分别用于读取 JSON 文件和将 TypeScript 类型定义字符串写入文件。
最后定义一个名为 jsonToTsFile
的函数,该函数接收命令行参数并将其传递给 jsonToTs
函数,然后将生成的 TypeScript 类型定义字符串保存到文件中。如果命令行参数中指定了不保存文件,则该函数将直接将 TypeScript 类型定义字符串输出到控制台。
const fs = require('fs-extra');
const commander = require('commander');
/**
* 将 JSON 数据转换为 TypeScript 类型定义
* @param {Object} object - 要转换的 JSON 对象
* @param {string} [name=JsonType] - 转换后的类型名称
* @param {string} [namespace] - 转换后的命名空间
* @returns {string} - 转换后的 TypeScript 类型定义字符串
*/
function jsonToTs(object, name = 'JsonType', namespace) {
const getType = value => {
let typeRes = ``;
if (Array.isArray(value)) {
value.forEach(item => {
let subType = getType(item);
if (typeRes.split('|').indexOf(subType) < 0) {
typeRes += subType
typeRes += "|"
}
})
typeRes = typeRes.substring(0, typeRes.length - 1)
return `(${typeRes})[]`;
}
if (typeof value === 'object' && value !== null) {
const props = Object.entries(value)
.map(([key, val]) => `${key}: ${getType(val)}`)
.join('; ');
return `{ ${props} }`;
}
return typeof value;
};
const type = getType(object);
const declaration = `interface ${name} ${type}`;
return namespace ? `namespace ${namespace} { \r\n ${declaration} \r\n}` : declaration;
}
/**
* 读取文件并解析成 JSON 对象
* @param {string} path - 文件路径
* @returns {Promise<Object>} - JSON 对象
*/
async function readJson(path) {
const content = await fs.readFile(path, 'utf8');
return JSON.parse(content);
}
/**
* 将 TypeScript 类型定义字符串写入文件
* @param {string} content - TypeScript 类型定义字符串
* @param {string} path - 文件路径
* @returns {Promise<void>}
*/
async function writeTs(content, path) {
await fs.writeFile(path, content, 'utf8');
}
/**
* 将 JSON 数据转换为 TypeScript 类型定义
* @param {string} inputPath - 输入 JSON 文件路径
* @param {string} outputPath - 输出 TypeScript 文件路径
* @param {string} [options.name=JsonType] - 转换后的类型名称
* @param {string} [options.namespace] - 转换后的命名空间
* @param {boolean} [options.noFile] - 不将 TypeScript 类型定义保存为文件
* @returns {Promise<void>}
*/
async function jsonToTsFile(inputPath, outputPath, options) {
const { name, namespace, noFile } = options
try {
const object = await readJson(inputPath);
const type = jsonToTs(object, name, namespace);
if (noFile) {
console.log(type);
} else {
await writeTs(type, outputPath);
console.log(`Type definition saved to ${outputPath}`);
}
} catch (err) {
console.error(err.message);
}
}
const program = new commander.Command();
program
.arguments('<input> <output>')
.option('--no-file', 'do not save to file')
.option('-s, --namespace <namespace>', 'type namespace')
.option('-n, --name <name>', 'type name', 'JsonType')
.action(jsonToTsFile);
program.parse(process.argv);
六、写在最后
这个工具可以极大地提高在 TypeScript 项目中编写类型声明的效率。通过输入一个 JSON 数据,它可以自动生成对应的 TypeScript 类型定义,支持复杂类型,如数组和对象,并支持自定义类型名称和命名空间。此外,还可以选择将转换后的 TypeScript 类型定义保存为文件。
来源:https://juejin.cn/post/7217304466077024312


猜你喜欢
- gzip文件读写的时候需要用到Python的gzip模块。具体使用如下:# -*- coding: utf-8 -*-import gzip
- 阅读上一篇:FrontPage2002简明教程三:网页布局 网页的强大之处就在它的超链接,在浏览器中通过点击网页中的超链接,可以很方便地打开
- 概况在开发过程中,我们一般直接用Python命令直接运行Flask程序。这样的运行只适合我们开发,方便我们调试。一旦程序部署到线上,这样运行
- 在上一篇博客中,已经将环境搭建好了。现在,我们利用搭建的环境来运行一条测试脚本,脚本中启动一个计算器的应用,并实现加法的运算。创建模拟器在运
- 普通滑动验证以http://admin.emaotai.cn/login.aspx为例这类验证码只需要我们将滑块拖动指定位置,处理起来比较简
- 什么是formset我们知道forms组件是用来做表单验证,更准确一点说,forms组件是用来做数据库表中一行记录的验证。有forms组件不
- 注册模块default.asp 代码如下:<!DOCTYPE html PUBLIC "-//
- 前言又见面了,小伙伴儿们,发现最近大家喜欢看一些简单的小案例?!咳咳,下面进入正题。每个人的电脑里都会有不想让别人知道的隐私,或者是上班时间
- 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档前言能用golang就用golang..这配置很麻烦 提示:以下是本篇文章正
- 就像HTML或者Python,Django模板语言同样提供代码注释。 注释使用 {# #} :{# This is a comment #}
- theme: channing-cyan网页伪静态将 * 页伪装成静态网页,可以提升网页被搜索引擎检索道德概率表现形式为:网址看着像是一个具
- 一、.NET Framework Data Provider for SQL Server类型:.NET Framework类库使用:Sys
- element-UI el-table树形数据 修改小三角图标el-table树形数据 默认样式有下级没展开是▸ 有下级展开了是▾ 没有下级
- 什么是python的装饰器?网络上的定义:装饰器就是一函数,用来包装函数的函数,用来修饰原函数,将其重新赋值给原来的标识符,并永久的丧失原函
- 在开始编写应用之前,我们先从最基本的程序开始。就像你造房子之前不知道什么是地基一样,编写程序也不知道如何开始。因此,在本节中,我们要学习用最
- 本文实例讲述了Python实现在matplotlib中两个坐标轴之间画一条直线光标的方法。分享给大家供大家参考。具体如下:看看下面的例子和效
- python matplotlib画图使用colorbar工具自定义颜色 colorbar(draw colorbar without an
- 利用numpy库(缺点:有缺失值就无法读取)读:import numpy my_matrix = numpy.loadtxt(open(&q
- 计划来实现一个便签墙系列,这个东西做简单也简单,往复杂了做功能也很多,记录一下从零到有的开发过程吧,希望最后能把这个项目做得很完善。首先是前
- 目录先明确几点赋值浅拷贝深拷贝总结先明确几点不可变类型:该数据类型对象所指定内存中的值不可以被改变。(1)、在改变某个对象的值时,由于其内存