繁体   English   中英

如何将 json 转换为 typescript 接口?

[英]How to convert a json to a typescript interface?

给定 api 的 JSON output :

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

我想为 typescript 定义接口:

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

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

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

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

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

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

文件sample.ts

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

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

 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
});

找到一个 npm 包,可以将任意没有 schema 的 JSON 文件转换为 TS 接口: https : //www.npmjs.com/package/json-to-ts

作者还提供了一个VSCode插件。

您可以使用 npm 模块代替 Web 托管解决方案:

https://www.npmjs.com/package/json-schema-to-typescript

如果您的 JSON 来自 HTTP API 并且该 API 具有 swagger 代码定义,您可以生成一个 TypeScript 客户端:

https://github.com/swagger-api/swagger-codegen#api-clients

如果您的 json 来自 Java ot .Net 支持,您可以从 Java 或 C# 类生成 TypeScript:

http://type.litesolutions.net

https://github.com/raphaeljolivet/java2typescript

仅使用sedtsc

sed -i.ts '1s@^@const foo = @' sample.json
tsc sample.json.ts --emitDeclarationOnly --declaration

有一个非常有用的 VS 代码扩展: https://github.com/quicktype/quicktype

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM