Skip to content

Commit 1fdea10

Browse files
author
k.golikov
committed
Add JSON to TypeScript converter, WIP 2
1 parent c26c2e2 commit 1fdea10

File tree

7 files changed

+166
-5
lines changed

7 files changed

+166
-5
lines changed

src/pages/jsonToTypeScriptPage/JsonToTypeScriptPage.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { editor } from 'monaco-editor';
88
import { useDebouncedMemo } from '../../hooks/debouncedMemo';
99
import getTypeScriptType from './utils/getTypeScriptType';
1010
import parseJsonObject from './utils/parseJsonObject';
11+
import ExportType from './types/ExportType';
12+
import getAllTypeScriptTypeDeclarations from './utils/getAllTypeScriptTypeDeclarations';
1113

1214
const jsonEditorOptions: editor.IStandaloneEditorConstructionOptions = {
1315
minimap: { enabled: false }
@@ -29,7 +31,7 @@ const JsonToTypeScriptPage = () => {
2931
}
3032

3133
const typeScriptType = getTypeScriptType('Root', parseJsonObject(JSON.parse(json)));
32-
return 'const result = ' + JSON.stringify(typeScriptType, undefined, 4);
34+
return getAllTypeScriptTypeDeclarations(typeScriptType, 'Root', ExportType.ES_MODULE);
3335
},
3436
[json],
3537
50
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
enum ExportType {
2+
NONE = 'NONE',
3+
ES_MODULE = 'ES_MODULE',
4+
COMMONJS = 'COMMONJS'
5+
}
6+
7+
export default ExportType;
Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,93 @@
11
import { JsonPrimitive } from './json';
22
import { IObject } from './common';
3+
import getTypeScriptTypeReference from '../utils/getTypeScriptTypeReference';
4+
import mapObject from '../../../utils/mapObject';
5+
import ExportType from './ExportType';
36

4-
export class TypeScriptUnknown {
7+
export interface ITypeScriptType {
8+
stringifyReference(): string;
9+
}
10+
11+
export interface IDeclarableTypeScriptType {
12+
readonly name: string;
13+
stringifyDeclaration(exportType?: ExportType): string;
14+
stringifyDeclarationBody(): string;
15+
}
16+
17+
export class TypeScriptUnknown implements ITypeScriptType {
518
public readonly isUnknown = true;
19+
20+
stringifyReference(): string {
21+
return 'unknown';
22+
}
623
}
724

8-
export class TypeScriptInterface extends IObject<TypeScriptType> {
25+
export class TypeScriptInterface extends IObject<TypeScriptType> implements ITypeScriptType, IDeclarableTypeScriptType {
926
public constructor(public readonly name: string, public readonly fields: Record<string, TypeScriptType>) {
1027
super(fields);
1128
}
29+
30+
stringifyDeclaration(exportType?: ExportType): string {
31+
return `${getExportKeyword(exportType)}interface ${this.name} ${this.stringifyDeclarationBody()}`;
32+
}
33+
34+
stringifyDeclarationBody(): string {
35+
return (
36+
'{\n' +
37+
mapObject(this.fields, (key, value) => {
38+
return ` ${key}: ${getTypeScriptTypeReference(value)};`;
39+
}).join('\n') +
40+
'\n}'
41+
);
42+
}
43+
44+
stringifyReference(): string {
45+
return this.name;
46+
}
1247
}
1348

14-
export class TypeScriptArray {
49+
export class TypeScriptArray implements ITypeScriptType {
1550
public constructor(public readonly type: TypeScriptType) {}
51+
52+
stringifyReference(): string {
53+
const typeReference = getTypeScriptTypeReference(this.type);
54+
55+
if (this.type instanceof TypeScriptUnion) {
56+
return `Array<${typeReference}>`;
57+
}
58+
59+
return `${typeReference}[]`;
60+
}
61+
}
62+
63+
export class TypeScriptUnion implements ITypeScriptType {
64+
//, IDeclarableTypeScriptType {
65+
public constructor(public readonly name: string, public readonly types: TypeScriptType[]) {}
66+
67+
// stringifyDeclaration(exportType?: ExportType): string {
68+
// return `${getExportKeyword(exportType)}type ${this.name} = ${this.stringifyDeclarationBody()};`;
69+
// }
70+
71+
stringifyDeclarationBody(): string {
72+
return this.types.map(getTypeScriptTypeReference).join(' | ');
73+
}
74+
75+
stringifyReference(): string {
76+
return this.stringifyDeclarationBody();
77+
}
1678
}
1779

18-
export class TypeScriptUnion {
80+
//TODO add tuples support
81+
export class TypeScriptTuple implements ITypeScriptType {
1982
public constructor(public readonly name: string, public readonly types: TypeScriptType[]) {}
83+
84+
stringifyDeclarationBody(): string {
85+
return '[' + this.types.map(getTypeScriptTypeReference).join(', ') + ']';
86+
}
87+
88+
stringifyReference(): string {
89+
return this.stringifyDeclarationBody();
90+
}
2091
}
2192

2293
export type TypeScriptType =
@@ -25,3 +96,11 @@ export type TypeScriptType =
2596
| TypeScriptArray
2697
| TypeScriptUnion
2798
| TypeScriptUnknown;
99+
100+
const getExportKeyword = (exportType: ExportType = ExportType.NONE) => {
101+
return {
102+
[ExportType.NONE]: '',
103+
[ExportType.ES_MODULE]: 'export ',
104+
[ExportType.COMMONJS]: 'module.exports = '
105+
}[exportType];
106+
};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import {
2+
IDeclarableTypeScriptType,
3+
TypeScriptArray,
4+
TypeScriptType,
5+
TypeScriptUnion,
6+
TypeScriptUnknown
7+
} from '../types/typescript';
8+
import { isString } from 'lodash';
9+
import mapObject from '../../../utils/mapObject';
10+
11+
const getAllTypeScriptTypeDeclarableTypes = (type: TypeScriptType): IDeclarableTypeScriptType[] => {
12+
if (isString(type) || type instanceof TypeScriptUnknown) {
13+
return []; //getTypeScriptTypeDeclaration(type, name, exportType)
14+
}
15+
16+
if (type instanceof TypeScriptUnion) {
17+
return type.types.flatMap((innerType) => getAllTypeScriptTypeDeclarableTypes(innerType));
18+
}
19+
20+
if (type instanceof TypeScriptArray) {
21+
return getAllTypeScriptTypeDeclarableTypes(type.type);
22+
}
23+
24+
return [
25+
...mapObject(type.fields, (fieldName, fieldType) => getAllTypeScriptTypeDeclarableTypes(fieldType)).flatMap(
26+
(value) => value
27+
),
28+
type
29+
];
30+
};
31+
32+
export default getAllTypeScriptTypeDeclarableTypes;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import getAllTypeScriptTypeDeclarableTypes from './getAllTypeScriptTypeDeclarableTypes';
2+
import { TypeScriptType } from '../types/typescript';
3+
import getTypeScriptTypeDeclaration from './getTypeScriptTypeDeclaration';
4+
import ExportType from '../types/ExportType';
5+
6+
const getAllTypeScriptTypeDeclarations = (type: TypeScriptType, name: string, exportType?: ExportType) => {
7+
return getAllTypeScriptTypeDeclarableTypes(type)
8+
.map((declarable) => getTypeScriptTypeDeclaration(declarable as TypeScriptType, name, exportType))
9+
.join('\n\n');
10+
};
11+
12+
export default getAllTypeScriptTypeDeclarations;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { TypeScriptType } from '../types/typescript';
2+
import { isString } from 'lodash';
3+
import ExportType from '../types/ExportType';
4+
5+
const getTypeScriptTypeDeclaration = (type: TypeScriptType, name: string, exportType?: ExportType): string => {
6+
if (isString(type)) {
7+
return `type ${name} = ${type};`;
8+
}
9+
10+
if ('stringifyDeclaration' in type) {
11+
return type.stringifyDeclaration(exportType);
12+
}
13+
14+
return '';
15+
};
16+
17+
export default getTypeScriptTypeDeclaration;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { TypeScriptType } from '../types/typescript';
2+
import { isString } from 'lodash';
3+
4+
const getTypeScriptTypeReference = (type: TypeScriptType): string => {
5+
if (isString(type)) {
6+
return type;
7+
}
8+
9+
return type.stringifyReference();
10+
};
11+
12+
export default getTypeScriptTypeReference;

0 commit comments

Comments
 (0)