如何将json转换为打字稿界面? [英] How to convert a json to a typescript interface?

查看:115
本文介绍了如何将json转换为打字稿界面?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给出api的JSON输出:

Given a JSON output of an api:

{
    "id": 13,
    "name": "horst",
    "cars": [{
        "brand": "VW",
        "maxSpeed": 120,
        "isWastingGazoline": true, 
    }]
}

我想为typescript定义接口:

I would like to define interfaces for typescript:

export interface Car {
    brand: string;
    maxSpeed: number;
    isWastingGazoline: boolean;
}

export interface RaceCarDriver {
    id: number;
    name: string;
    cars: Car[];
}

但我不希望他们手动输入,我宁愿拥有脚本为我生成它们。

Yet I don't want them to type them manually, I rather have a script generate them for me.

如何将json转换为typescript接口?我也不想使用像 json2ts 这样的网络服务。

How can I convert json into typescript interfaces? I also don't want to use webservices like json2ts.

推荐答案

您可以使用编写脚本typescript编译器API 及其推断类型的能力。我真的很惊讶它是多么容易。

You can write the script using typescript compiler API and its ability to infer types. I was really surprised how easy it was.

您必须包装样本数据,使其可以作为打字稿代码进行编译。该脚本将选择所有变量声明并尝试为它们打印推断类型。它使用变量名和属性名为类型分配名称,如果两个对象具有相同名称的属性,它将从第一个中选择类型。因此,如果这些类型实际上不同(将修复程序留作练习),它将无法工作。对于您的JSON输出,数据样本将类似于

You have to wrap your sample data to make it compile-able as typescript code. The script will pick all variable declarations and try to print inferred types for them. It uses variable names and property names for assigning names to types, and if two objects have a property with the same name, it will pick the type from the first one. So it will not work in case these types are actually different (the fix is left as an exercise). For your JSON output, the data sample will look like

文件 sample.ts

let raceCarDriver = {
    "id": 13,
    "name": "horst",
    "cars": [{
        "brand": "VW",
        "maxSpeed": 120,
        "isWastingGazoline": true,
    }]
};

脚本使用Typescript 2.1进行测试(刚刚发布):

The script was tested with Typescript 2.1 (just released):

 npm i typescript
 npm i @types/node
 ./node_modules/.bin/tsc --lib es6 print-inferred-types.ts
 node print-inferred-types.js sample.ts

输出:

export interface RaceCarDriver {
    id: number;
    name: string;
    cars: Car[];
}
export interface Car {
    brand: string;
    maxSpeed: number;
    isWastingGazoline: boolean;
}

以下是脚本: print-inferred-types .ts

import * as ts from "typescript";

let fileName = process.argv[2];

function printInferredTypes(fileNames: string[], options: ts.CompilerOptions): void {
    let program = ts.createProgram(fileNames, options);
    let checker = program.getTypeChecker();

    let knownTypes: {[name: string]: boolean} = {};
    let pendingTypes: {name: string, symbol: ts.Symbol}[] = [];

    for (const sourceFile of program.getSourceFiles()) {
        if (sourceFile.fileName == fileName) {
            ts.forEachChild(sourceFile, visit);
        }
    }

    while (pendingTypes.length > 0) {
        let pendingType = pendingTypes.shift();
        printJsonType(pendingType.name, pendingType.symbol);
    }


    function visit(node: ts.Node) {
        if (node.kind == ts.SyntaxKind.VariableStatement) {
            (<ts.VariableStatement>node).declarationList.declarations.forEach(declaration => {
                if (declaration.name.kind == ts.SyntaxKind.Identifier) {
                    let identifier = <ts.Identifier>declaration.name;
                    let symbol = checker.getSymbolAtLocation(identifier);
                    if (symbol) {
                        let t = checker.getTypeOfSymbolAtLocation(symbol, identifier);
                        if (t && t.symbol) {
                            pendingTypes.push({name: identifier.text, symbol: t.symbol});
                        }
                    }
                }
            });
        }
    }

    function printJsonType(name: string, symbol: ts.Symbol) {
        if (symbol.members) {
            console.log(`export interface ${capitalize(name)} {`);
            Object.keys(symbol.members).forEach(k => {
                let member = symbol.members[k];
                let typeName = null;
                if (member.declarations[0]) {
                    let memberType = checker.getTypeOfSymbolAtLocation(member, member.declarations[0]);
                    if (memberType) {
                        typeName = getMemberTypeName(k, memberType);
                    }
                }
                if (!typeName) {
                    console.log(`// Sorry, could not get type name for ${k}!`);
                } else {
                    console.log(`    ${k}: ${typeName};`);
                }
            });
            console.log(`}`);
        }
    }

    function getMemberTypeName(memberName: string, memberType: ts.Type): string | null {
        if (memberType.flags == ts.TypeFlags.String) {
            return 'string';
        } else if (memberType.flags == ts.TypeFlags.Number) {
            return 'number';
        } else if (0 !== (memberType.flags & ts.TypeFlags.Boolean)) {
            return 'boolean';
        } else if (memberType.symbol) {
            if (memberType.symbol.name == 'Array' && (<ts.TypeReference>memberType).typeArguments) {
                let elementType = (<ts.TypeReference>memberType).typeArguments[0];
                if (elementType && elementType.symbol) {
                    let elementTypeName = capitalize(stripS(memberName));
                    if (!knownTypes[elementTypeName]) {
                        knownTypes[elementTypeName] = true;
                        pendingTypes.push({name: elementTypeName, symbol: elementType.symbol});
                    }
                    return `${elementTypeName}[]`;
                }
            } else if (memberType.symbol.name == '__object') {
                let typeName = capitalize(memberName);
                if (!knownTypes[typeName]) {
                    knownTypes[typeName] = true;
                    pendingTypes.push({name: typeName, symbol: memberType.symbol});
                }
                return typeName;
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    function capitalize(n: string) {
        return n.charAt(0).toUpperCase() + n.slice(1);
    }
    function stripS(n: string) {
        return n.endsWith('s') ? n.substring(0, n.length - 1) : n;
    }
}

printInferredTypes([fileName], {
    noEmitOnError: true, noImplicitAny: true,
    target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS
});

这篇关于如何将json转换为打字稿界面?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆