0

0

将TypeScript推断类型转换为JSON模式表示的编程指南

霞舞

霞舞

发布时间:2025-10-11 10:20:12

|

963人浏览过

|

来源于php中文网

原创

将TypeScript推断类型转换为JSON模式表示的编程指南

本文深入探讨如何利用typescript编译器api,将typescript文件中导出的常量对象的推断类型结构,以编程方式转换为json格式的类型模式表示。我们将详细讲解如何解析抽象语法树(ast)、获取精确的类型信息,并递归构建所需的类型描述json,从而实现对类型而非运行时值的结构化表示。

在TypeScript开发中,我们经常会遇到需要将代码中定义的类型结构,以某种标准化的数据格式(例如JSON)进行表示或传输的场景。这不同于简单地将一个JavaScript对象的运行时值转换为JSON字符串。用户期望的是一个反映类型名称(如"string"、"number")的JSON结构,而非实际的字面量值。要实现这一目标,我们需要借助TypeScript编译器API来深入解析源代码,提取其类型元数据。

理解类型转换的核心挑战:类型与值的区分

考虑以下TypeScript文件 my-ts-file.ts:

// my-ts-file.ts
const inner = {
  hello: "world",
};

export const value = {
  prop1: "hello",
  prop2: "world",
  prop3: 42,
  prop4: inner,
};

当TypeScript编译器处理这段代码时,它会为 value 变量推断出如下类型:

const value: {
    prop1: string;
    prop2: string;
    prop3: number;
    prop4: {
        hello: string;
    };
}

用户希望得到的是一个JSON结构,其中每个属性的值是其对应的TypeScript类型名称,例如:

{
    "prop1": "string",
    "prop2": "string",
    "prop3": "number",
    "prop4": {
        "hello": "string"
    }
}

简单地使用 JSON.parse(JSON.stringify(value)) 只能将 value 的运行时值转换为JSON,其结果将是:

{
    "prop1": "hello",
    "prop2": "world",
    "prop3": 42,
    "prop4": {
        "hello": "world"
    }
}

这显然不符合将“推断类型”转换为JSON模式的需求。因此,我们需要一个能够访问和解释TypeScript类型系统的工具,即TypeScript编译器API。

借助TypeScript编译器API解析类型信息

TypeScript编译器API提供了一套强大的接口,允许我们以编程方式与TypeScript代码进行交互。它能够解析源代码文件,构建抽象语法树(AST),并执行类型检查,从而获取到变量、函数、类等的详细类型信息。

实现将推断类型转换为JSON模式的关键步骤如下:

  1. 创建TypeScript程序实例: 加载目标TypeScript文件并建立编译器上下文。
  2. 获取类型检查器: 类型检查器是访问所有类型信息的入口。
  3. 定位目标变量: 在AST中找到我们感兴趣的 export const value 变量声明。
  4. 提取变量类型: 使用类型检查器获取该变量的精确推断类型。
  5. 递归遍历类型结构: 将获取到的类型对象递归地转换为我们期望的JSON模式。

实现步骤详解

首先,确保你的项目中安装了 typescript 包:

腾讯交互翻译
腾讯交互翻译

腾讯AI Lab发布的一款AI辅助翻译产品

下载
npm install typescript

接下来,我们将创建一个Node.js脚本(例如 type-extractor.ts)来实现上述逻辑。

1. 目标TypeScript文件 (my-ts-file.ts)

// my-ts-file.ts
const inner = {
  hello: "world",
};

export const value = {
  prop1: "hello",
  prop2: "world",
  prop3: 42,
  prop4: inner,
};

export const anotherValue = {
  items: [1, 2, 3],
  status: "active",
};

2. 类型提取脚本 (type-extractor.ts)

import * as ts from 'typescript';
import * as path from 'path';

/**
 * 将TypeScript类型对象转换为其对应的字符串名称。
 * @param typeChecker TypeScript类型检查器实例。
 * @param type TypeScript类型对象。
 * @returns 类型的字符串表示(如 "string", "number", "boolean", "object", "array")。
 */
function getTypeString(typeChecker: ts.TypeChecker, type: ts.Type): string {
    if (type.flags & ts.TypeFlags.String) {
        return "string";
    }
    if (type.flags & ts.TypeFlags.Number) {
        return "number";
    }
    if (type.flags & ts.TypeFlags.Boolean) {
        return "boolean";
    }
    if (type.flags & ts.TypeFlags.Null) {
        return "null";
    }
    if (type.flags & ts.TypeFlags.Undefined) {
        return "undefined";
    }
    if (type.flags & ts.TypeFlags.Any) {
        return "any";
    }
    if (type.flags & ts.TypeFlags.Void) {
        return "void";
    }
    if (type.flags & ts.TypeFlags.Unknown) {
        return "unknown";
    }
    if (type.flags & ts.TypeFlags.BigInt) {
        return "bigint";
    }
    if (type.flags & ts.TypeFlags.ESSymbol) {
        return "symbol";
    }

    // 检查是否为数组类型
    if (typeChecker.isArrayLikeType(type)) {
        const elementType = typeChecker.getTypeArguments(type as ts.TypeReference)[0];
        if (elementType) {
            return getTypeString(typeChecker, elementType) + "[]";
        }
        return "any[]"; // 无法确定元素类型
    }

    // 检查是否为对象类型(包括字面量对象和接口)
    if (type.flags & ts.TypeFlags.Object || typeChecker.getPropertiesOfType(type).length > 0) {
        return "object";
    }

    // 默认返回类型文本,这对于更复杂的类型(如联合类型、字面量类型)可能更精确
    return typeChecker.typeToString(type);
}

/**
 * 递归地将TypeScript类型转换为JSON模式表示。
 * @param typeChecker TypeScript类型检查器实例。
 * @param type TypeScript类型对象。
 * @returns 对应的JSON模式对象。
 */
function convertTypeToJsonSchema(typeChecker: ts.TypeChecker, type: ts.Type): any {
    // 检查是否为原始类型
    const primitiveType = getTypeString(typeChecker, type);
    if (primitiveType !== "object" && !primitiveType.endsWith("[]")) {
        return primitiveType;
    }

    // 检查是否为数组类型
    if (typeChecker.isArrayLikeType(type)) {
        const elementType = typeChecker.getTypeArguments(type as ts.TypeReference)[0];
        return [elementType ? convertTypeToJsonSchema(typeChecker, elementType) : "any"];
    }

    // 处理对象类型
    const properties: { [key: string]: any } = {};
    const symbol = type.getSymbol();

    if (symbol && symbol.declarations && symbol.declarations.length > 0) {
        // 尝试从声明中获取属性(例如接口或类型别名)
        const declaration = symbol.declarations[0];
        if (ts.isInterfaceDeclaration(declaration) || ts.isTypeAliasDeclaration(declaration)) {
            typeChecker.getPropertiesOfType(type).forEach(prop => {
                const propType = typeChecker.getTypeOfSymbolAtLocation(prop, declaration);
                properties[prop.getName()] = convertTypeToJsonSchema(typeChecker, propType);
            });
            return properties;
        }
    }

    // 对于匿名对象字面量,直接获取其属性
    typeChecker.getPropertiesOfType(type).forEach(prop => {
        const propType = typeChecker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration || prop.declarations[0]);
        properties[prop.getName()] = convertTypeToJsonSchema(typeChecker, propType);
    });

    return properties;
}

/**
 * 提取指定文件中导出变量的类型并转换为JSON模式。
 * @param filePath 目标TypeScript文件的路径。
 * @param variableName 目标导出变量的名称。
 * @returns 包含类型模式的JSON对象,如果未找到则为null。
 */
function extractExportedVariableTypeAsJson(filePath: string, variableName: string): any | null {
    const program = ts.createProgram([filePath], {
        target: ts.ScriptTarget.ESNext,
        module: ts.ModuleKind.CommonJS,
        strict: true,
        // 如果你的项目有tsconfig.json,可以这样加载
        // project: path.dirname(filePath)
    });

    const sourceFile = program.getSourceFile(filePath);
    if (!sourceFile) {
        console.error(`Error: Could not find source file at ${filePath}`);
        return null;
    }

    const typeChecker = program.getTypeChecker();
    let result: any | null = null;

    ts.forEachChild(sourceFile, node => {
        // 查找 `export const variableName = ...`
        if (ts.isVariableStatement(node) && node.modifiers &&
            node.modifiers.some(m => m.kind === ts.SyntaxKind.ExportKeyword)) {
            node.declarationList.declarations.forEach(declaration => {
                if (ts.isIdentifier(declaration.name) && declaration.name.text === variableName) {
                    const symbol = typeChecker.getSymbolAtLocation(declaration.name);
                    if (symbol) {
                        const type = typeChecker.getTypeOfSymbolAtLocation(symbol, declaration.name);
                        result = convertTypeToJsonSchema(typeChecker, type);
                    }
                }
            });
        }
    });

    return result;
}

// 示例用法
const tsFilePath = path.join(__dirname, 'my-ts-file.ts');

const valueTypeJson = extractExportedVariableTypeAsJson(tsFilePath, 'value');
if (valueTypeJson) {
    console.log(`Type schema for 'value':\n${JSON.stringify(valueTypeJson, null, 2)}`);
}

const anotherValueTypeJson = extractExportedVariableTypeAsJson(tsFilePath, 'anotherValue');
if (anotherValueTypeJson) {
    console.log(`\nType schema for 'anotherValue':\n${JSON.stringify(anotherValueTypeJson, null, 2)}`);
}

运行结果:

Type schema for 'value':
{
  "prop1": "string",
  "prop2": "string",
  "prop3": "number",
  "prop4": {
    "hello": "string"
  }
}

Type schema for 'anotherValue':
{
  "items": [
    "number"
  ],
  "status": "string"
}

代码解析

  1. extractExportedVariableTypeAsJson 函数:

    • 创建 ts.Program 实例来加载和编译TypeScript文件。
    • 获取 ts.SourceFile 和 ts.TypeChecker,它们是进行AST遍历和类型检查的核心。
    • 遍历源文件的AST,查找带有 export 关键字且名称匹配 variableName 的 const 变量声明。
    • 通过 typeChecker.getSymbolAtLocation 获取变量的符号(Symbol),再通过 typeChecker.getTypeOfSymbolAtLocation 获取其完整的 ts.Type 对象。
    • 将获取到的 ts.Type 对象传递给 convertTypeToJsonSchema 进行递归转换。
  2. convertTypeToJsonSchema 函数:

    • 这是核心的递归函数。它接收 ts.Type 对象并尝试将其转换为JSON模式。
    • 首先通过 getTypeString 尝试识别基本类型(string, number, boolean等)和数组类型。
    • 如果类型是对象(ts.TypeFlags.Object),它会获取该类型的所有属性 (typeChecker.getPropertiesOfType)。
    • 对于每个属性,它递归调用 convertTypeToJsonSchema 来获取其子类型模式,并将其添加到结果对象中。
    • 对于数组类型,它会尝试获取数组的元素类型 (typeChecker.getTypeArguments),并递归处理元素类型。
  3. getTypeString 函数:

    • 这是一个辅助函数,用于将 ts.Type 对象映射到其常见的字符串表示。
    • 它通过检查 ts.TypeFlags 来判断基本类型。
    • 特别处理了数组类型 (typeChecker.isArrayLikeType),以返回如 "string[]" 的格式。
    • 对于复杂或无法直接映射为基本类型的对象,它会返回 "object" 或通过 typeChecker.typeToString 返回其文本表示。

注意事项与进阶考量

  • 复杂类型处理: 上述示例主要处理了基本类型、对象字面量和数组。对于更复杂的TypeScript类型,例如:
    • 联合类型 (string | number): 需要在JSON模式中表示为 ["string", "number"] 或 { "oneOf": ["string", "number"] }。
    • 交叉类型 (TypeA & TypeB): 可能需要合并属性。
    • 泛型 (Array<T>)、接口 (interface MyInterface)、类型别名 (type MyType = ...): 需要更复杂的逻辑来解析和展开这些类型。
    • 枚举 (enum MyEnum): 可以表示为字符串或数字的列表。
    • 字面量类型 ("hello", 42): 可以直接表示为对应的字符串或数字。 这些情况需要扩展 convertTypeToJsonSchema 函数,根据 ts.Type 对象的更多属性和 ts.TypeFlags 来判断和处理。
  • 性能考量: 对于大型项目,使用TypeScript编译器API进行完整的程序解析和类型检查可能是一个耗时的操作。如果只需要处理少量文件或特定类型的导出,可以考虑优化程序创建和文件解析的策略。
  • 错误处理: 在实际应用中,需要增加健壮的错误处理机制,例如文件不存在、语法错误、变量未找到等情况。
  • JSON Schema标准: 如果目标是生成符合JSON Schema标准的输出,那么上述 convertTypeToJsonSchema 函数需要进一步扩展,以包含 type, properties, items, required, enum 等JSON Schema关键字。例如,数组类型应表示为 { "type": "array", "items": { "type": "number" } }。
  • 现有工具: 对于生成JSON Schema的需求,社区中已有成熟的工具,如 ts-json-schema-generator,它们提供了更全面、更符合JSON Schema规范的解决方案。本文提供的方案更侧重于展示如何从底层利用TypeScript编译器API实现这一过程。

总结

通过TypeScript编译器API,我们可以深入到源代码的抽象语法树和类型系统中,以编程方式提取并分析类型信息。本文详细介绍了如何加载TypeScript文件、获取类型检查器、定位目标变量并递归地将其推断类型转换为自定义的JSON模式表示。理解并掌握这一过程,对于构建代码分析工具、类型文档生成器或动态表单生成器等场景具有重要的意义。虽然处理所有复杂的TypeScript类型需要更精细的逻辑,但本文提供的基础框架为进一步的扩展和定制奠定了坚实的基础。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
TypeScript工程化开发与Vite构建优化实践
TypeScript工程化开发与Vite构建优化实践

本专题面向前端开发者,深入讲解 TypeScript 类型系统与大型项目结构设计方法,并结合 Vite 构建工具优化前端工程化流程。内容包括模块化设计、类型声明管理、代码分割、热更新原理以及构建性能调优。通过完整项目示例,帮助开发者提升代码可维护性与开发效率。

47

2026.02.13

TypeScript全栈项目架构与接口规范设计
TypeScript全栈项目架构与接口规范设计

本专题面向全栈开发者,系统讲解基于 TypeScript 构建前后端统一技术栈的工程化实践。内容涵盖项目分层设计、接口协议规范、类型共享机制、错误码体系设计、接口自动化生成与文档维护方案。通过完整项目示例,帮助开发者构建结构清晰、类型安全、易维护的现代全栈应用架构。

193

2026.02.25

json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

456

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

547

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

335

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

82

2025.09.10

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1030

2023.08.02

java中boolean的用法
java中boolean的用法

在Java中,boolean是一种基本数据类型,它只有两个可能的值:true和false。boolean类型经常用于条件测试,比如进行比较或者检查某个条件是否满足。想了解更多java中boolean的相关内容,可以阅读本专题下面的文章。

367

2023.11.13

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

76

2026.03.11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 6万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 3.4万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号