How to convert a json to a typescript interface?

前端 未结 3 1559
野趣味
野趣味 2021-01-03 08:17

Given a JSON output of an api:

{
    \"id\": 13,
    \"name\": \"horst\",
    \"cars\": [{
        \"brand\": \"VW\",
        \"maxSpeed\": 120,
        \"is         


        
相关标签:
3条回答
  • 2021-01-03 08:30

    Found a npm package that converts a arbitrary JSON file without a schema into a TS interface: https://www.npmjs.com/package/json-to-ts

    The author also provided a VSCode plugin.

    0 讨论(0)
  • 2021-01-03 08:35

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

    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

    file sample.ts

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

    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
    

    output:

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

    Here is the script: 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
    });
    
    0 讨论(0)
  • 2021-01-03 08:44

    You can use an npm module instead of the web hosted solution:

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

    If your JSON comes from an HTTP API and the API has an swagger code definition, you can generate a TypeScript client:

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

    If you json comes from a Java ot .Net backened, you can generate TypeScript from Java or C# classes:

    http://type.litesolutions.net

    https://github.com/raphaeljolivet/java2typescript

    0 讨论(0)
提交回复
热议问题