如何将 json 转换为 typescript 接口?

Posted

技术标签:

【中文标题】如何将 json 转换为 typescript 接口?【英文标题】:How to convert a json to a typescript interface? 【发布时间】:2017-04-25 12:55:22 【问题描述】:

给定一个 api 的 JSON 输出:


    "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[];

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

我也不想使用像MakeTypes 或json2ts 这样的网络服务。

【问题讨论】:

那么你是怎么做到的呢?我也在寻找解决方案 @Jaya 大多数时候我确实使用网络服务 ^^' 像这样:jvilk.com/MakeTypes 或这样:json2ts.com 感谢@k0pernikus 的快速回复! 【参考方案1】:

仅使用 sedtsc

sed '1s@^@const foo = @' sample.json > sample.$$.ts
tsc sample.$$.ts --emitDeclarationOnly --declaration
    const foo = 附加到文件开头 使用sed 将第一行(1)开头的(s)替换为const foo = 输出到sample.$$.ts 扩展名必须是 .ts$$ 扩展为 shell 进程 ID,对于不太可能覆盖您关心的内容的临时文件很方便 要求tsc 只发出一个.d.ts 分型文件 该文件几乎包含您想要的界面的所有内容。您可能需要替换一些字符串并以您想要的方式对其进行自定义,但大部分工作已经完成

【讨论】:

我不清楚您是否要将sample.json 转换为sample.json.ts 并且预期的结果是sample.json.ts。我还认为您的sed 命令不会创建sample.json.ts 文件,而是替换sample.json 的内容。不过我喜欢这种方法,请点赞。 注意tsc expexts 扩展名为.ts.tsx.d.ts 的文件。 IE。你不能这样做tsc /dev/stdin ... 一个人可能可以使用带有假扩展名的命名管道¯_(ツ)_/¯ @k0pernikus 你是对的,它会创建一个带有.ts 扩展名的备份。我会解决的【参考方案2】:

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

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

【讨论】:

相当整洁。我认为这应该是公认的答案。它甚至还有一个在线界面:jsontots.com。 VSCode 插件救了我的命。绝对应该是公认的答案。【参考方案3】:

您可以使用typescript compiler 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
);

【讨论】:

【参考方案4】:

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

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 或 .Net 支持,您可以从 Java 或 C# 类生成 TypeScript:

http://type.litesolutions.net

https://github.com/raphaeljolivet/java2typescript

【讨论】:

json-schema-to-typescript 将 JSON Schema 文件 (json-schema.org) 更改为 TypeScript 接口。这个问题是关于根据任意 JSON 文件推断 TypeScript 接口。 @JanAagaard 如果您仍在寻找模块,请在下面查看我的答案

以上是关于如何将 json 转换为 typescript 接口?的主要内容,如果未能解决你的问题,请参考以下文章

typescript - 将 json 转换为接口

将 TypeScript 对象转换为 JSON

如何将 TypeScript 文件的对象文字 AST 转换为实际代码?

使用 TypeScript 将 JSON(从 Sentry)转换为 HTML

Typescript / Angular2:将 JSON 转换为与 Observable 和 JSONP 接口

Angular:Typescript 将 JSON 响应转换为对象模型不起作用