基于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-extracommanderfs-extra 是一个简化了 Node.js 文件系统模块的封装,而 commander 是一个命令行工具的库,可以方便地解析命令行参数。

接下来定义一个函数 jsonToTs,用于将 JSON 数据转换为 TypeScript 类型定义字符串。该函数采用递归的方式遍历 JSON 数据,生成对应的 TypeScript 类型定义。如果 JSON 数据是数组,则递归处理其中的每个元素;如果是对象,则递归处理其中的每个属性。最终,该函数返回一个 TypeScript 类型定义字符串。

然后定义了两个异步函数,readJsonwriteTs,分别用于读取 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

标签:JS,JSON,TypeScript,类型声明
0
投稿

猜你喜欢

  • 浅谈SQL Server中的三种物理连接操作(性能比较)

    2024-01-25 22:11:56
  • python自定义异常实例详解

    2022-09-04 23:33:19
  • 设计能力决定权力

    2009-06-16 14:48:00
  • Python使用微信接入图灵机器人过程解析

    2022-05-29 04:03:14
  • Python进行特征提取的示例代码

    2021-04-07 15:59:12
  • 自己用的ASP分页函数

    2009-10-18 11:30:00
  • MySQL索引类型一览 让MySQL高效运行起来

    2010-04-22 16:52:00
  • MyBatis实现Mysql数据库分库分表操作和总结(推荐)

    2024-01-24 07:19:11
  • C#连接数据库的方法

    2024-01-21 16:28:40
  • golang 防缓存击穿singleflight的实现

    2024-05-09 09:55:23
  • PHPMyadmin2.10中文显示为乱码的解决办法

    2007-08-22 08:18:00
  • 怎么解决pycharm license Acti的方法

    2022-12-27 10:36:39
  • js格式化金额可选是否带千分位以及保留精度

    2023-10-06 04:03:14
  • innerHTML,outerHTML,innerText,outerText用法

    2008-02-15 12:22:00
  • php字符串过滤strip_tags()函数用法实例分析

    2023-09-04 14:05:00
  • python判断正负数方式

    2023-07-06 11:16:12
  • Django REST framework视图的用法

    2021-02-10 02:51:49
  • vue.js 表格分页ajax 异步加载数据

    2024-05-21 10:12:57
  • scala中停止循环的三种方式(推荐)

    2023-03-20 07:03:34
  • Javascript 字符串模板的简单实现

    2024-05-02 17:30:37
  • asp之家 网络编程 m.aspxhome.com