如何将 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】:仅使用 sed
和 tsc
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 文件的对象文字 AST 转换为实际代码?
使用 TypeScript 将 JSON(从 Sentry)转换为 HTML