如何将打字稿定义解析为json?

Posted

技术标签:

【中文标题】如何将打字稿定义解析为json?【英文标题】:How to parse typescript definition to json? 【发布时间】:2017-01-28 01:03:51 【问题描述】:

我想把我的typescript定义解析成json,比如

namespace call.common 
    namespace login 
        interface Request 
          userName: string;
          password: string;
        

        interface Response 
            isLogin: boolean
        
    

json 可能是这样的:


    namespace: "call.common.login",
    interfaces: [
        
            "name": "Request",
            params: [
                name: "userName",
                type: "string",
            , 
                name: "password",
                type: "string"
            ]
        , 
            "name": "Response",
            params: [
                name: "isLogin",
                type: boolean
            ]
        
    ]

根据文档Using-the-Compiler-API我正在尝试使用编译器:

function parseGrpcTypings(fileName, options) 
    var program = ts.createProgram(fileName, options);
    var checker = program.getTypeChecker();

    const sourceFile = program.getSourceFiles()[0];
    ts.forEachChild(sourceFile, visit);

    function visit(node) 
        if (!isNodeExported(node)) 
            return;
        

        if (node.kind === ts.SyntaxKind.ModuleDeclaration) 
            ts.forEachChild(node, visit);
         else if (node.kind === ts.SyntaxKind.InterfaceDeclaration) 
            // how to parse interface
         else 
            // how to deal with namespace
        
    

    function isNodeExported(node) 
        return (node.flags & ts.NodeFlags.Export) !== 0 || (node.parent && node.parent.kind === ts.SyntaxKind.SourceFile);
    


parseGrpcTypings("/tmp/test.d.ts", 
    target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS
);

【问题讨论】:

你希望这个 json 看起来像什么?你想如何解析它?更多信息可能会有用... @NitzanTomer 类似于我发布的内容,但这是一个非常简单的示例 【参考方案1】:

根据您的代码,这里有一个 ts2json.js 模块,它将 .d.ts 文件转换为纯 javascript 对象:

const ts = require('typescript');

const PROPERTY_TYPES = 
    any: ts.SyntaxKind.AnyKeyword,
    boolean: ts.SyntaxKind.BooleanKeyword,
    number: ts.SyntaxKind.NumberKeyword,
    string: ts.SyntaxKind.StringKeyword,
;
class TSNode 
    constructor(name, type) 
        this.children = [];
        this.addChildren = (name, type) => 
            let node = new TSNode(name, type);
            this.children.push(node);
            return node;
        ;
        this.getType = () => this.type;
        this.getObject = () => 
            let map = ;
            map[this.name] = this.children.length
                ? this.children
                      .map(child => child.getObject())
                      .reduce((pv, child) => 
                          for (let key in child) 
                              if (pv.hasOwnProperty(key) || key in pv) 
                                  Object.assign(pv[key], child[key]);
                               else 
                                  pv[key] = child[key];
                              
                          
                          return pv;
                      , )
                : this.type;
            return map;
        ;
        this.name = name;
        this.type = type;
    

let visit = parent => node => 
    switch (node.kind) 
        case ts.SyntaxKind.ModuleDeclaration:
            let moduleName = node.name.text;
            ts.forEachChild(node, visit(parent.addChildren(moduleName)));
            break;
        case ts.SyntaxKind.ModuleBlock:
            ts.forEachChild(node, visit(parent));
            break;
        case ts.SyntaxKind.InterfaceDeclaration:
            let interfaceName = node.name.text;
            parent[interfaceName] = ;
            // console.log('interface');
            ts.forEachChild(node, visit(parent.addChildren(interfaceName)));
            break;
        case ts.SyntaxKind.PropertySignature:
            let propertyName = node.name;
            let propertyType = node.type;
            let arrayDeep = 0;
            let realPropertyName =
                'string' !== typeof propertyName && 'text' in propertyName
                    ? propertyName.text
                    : propertyName;
            while (propertyType.kind === ts.SyntaxKind.ArrayType) 
                arrayDeep++;
                propertyType = propertyType.elementType;
            
            if (propertyType.kind === ts.SyntaxKind.TypeReference) 
                let realPropertyType = propertyType.typeName;
                parent.addChildren(
                    realPropertyName,
                    'Array<'.repeat(arrayDeep) +
                        (realPropertyType.kind === ts.SyntaxKind.QualifiedName
                            ? realPropertyType.getText()
                            : 'text' in realPropertyType
                              ? realPropertyType.text
                              : realPropertyType) +
                        '>'.repeat(arrayDeep)
                );
             else 
                for (let type in PROPERTY_TYPES) 
                    if (propertyType.kind === PROPERTY_TYPES[type]) 
                        parent.addChildren(realPropertyName, type);
                        break;
                    
                
            
            break;
        default:
    
;

module.exports = function(filename, options) 
    const ROOT_NAME = 'root';
    const node = new TSNode(ROOT_NAME);

    let program = ts.createProgram([filename], options);
    let checker = program.getTypeChecker();
    let sourceFile = program.getSourceFiles()[1];

    ts.forEachChild(sourceFile, visit(node));

    return node.getObject()[ROOT_NAME];
;

您可以使用JSON.stringify 方法获取JSON 字符串。 以下 TypeScript 定义文件eg.d.ts

declare namespace School.Users 
    interface User 
        lastName: string;
        firstName: string;
        email: string;
    

    interface Student extends User 
        graduation: number;
        mainTeacher: Teacher;
    

    interface Teacher extends User 
        classes: Student[][];
        room: School.Building.Room;
    


declare namespace School.Building 
    interface Room 
        name: string;
    

可以通过以下方式解析为 JSON:

node -e "console.log(JSON.stringify(require('./ts2json.js')('./eg.d.ts', ), null, '\t'))"

输出:


    "School": 
        "Users": 
            "User": 
                "lastName": "string",
                "firstName": "string",
                "email": "string"
            ,
            "Student": 
                "graduation": "number",
                "mainTeacher": "Teacher"
            ,
            "Teacher": 
                "classes": "Array<Array<Student>>",
                "room": "School.Building.Room"
            
        ,
        "Building": 
            "Room": 
                "name": "string"
            
        
    

【讨论】:

不适用于typescript/lib/lib.dom.d.ts - 结果未定义【参考方案2】:

MrMaxie/ts-types-parser 可以从*.d.ts 文件中解析单一类型

const TSTypesParser = require('ts-types-parser');
const parser = new TSTypesParser();

// input file
parser.setSource('node_modules/typescript/lib/lib.dom.d.ts');
parser.setTarget('lib.dom.d.ts.js'); // required, will create empty file

// entry point
const mainType = 'CanvasRenderingContext2D';
parser.mainType(mainType);

const result = [];

parser.on('level-up', levelData => 
  const fullPath = `$mainType.$levelData.path.join('.')`;
  const r = `$fullPath /*: $levelData.type */`;
  result.push(r);
);

parser.on('done', () => 
  // restore output
  console.log = console._log;
  console.warn = console._warn;

  console.log(result.join('\n'));
)

// disable output from parser.run
console._log = console.log;
console._warn = console.warn;
console.log = () => ;
console.warn = () => ; 

parser.run();

【讨论】:

以上是关于如何将打字稿定义解析为json?的主要内容,如果未能解决你的问题,请参考以下文章

打字稿验证道具中的 JSON 解析

在javascript,打字稿中将json转换为csv

在打字稿中将 JSON 转换为字符串数组

打字稿路径无法解析

阿波罗联邦打字稿解析器参数

反应打字稿错误解析错误:'>'预期