Skip to content

Commit cc6b9b3

Browse files
committed
feat(typescript): Support interface heritage
Support for interfaces extending other interfaces, bi-directionally. Also optionally merge object intersections into single self-contained types, defaulting to true for JSON Schema and Open API. fix #15 fix #35
1 parent af74539 commit cc6b9b3

File tree

6 files changed

+261
-185
lines changed

6 files changed

+261
-185
lines changed

lib/bin/typeconv.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,15 @@ const oppaInstance =
148148
description: "Removes all annotations (descriptions, comments, ...)",
149149
default: false,
150150
} )
151+
.add( {
152+
name: 'merge-objects',
153+
type: 'boolean',
154+
description: [
155+
"When simplifying the types, merge intersections of objects",
156+
"into single self-contained objects.",
157+
"This defaults to true when converting to JSON Schema/OpenAPI."
158+
],
159+
} )
151160

152161
.group( {
153162
name: "TypeScript",
@@ -403,6 +412,7 @@ const {
403412
"output-directory": outputDirectory,
404413
"output-extension": outputExtension,
405414
"strip-annotations": doStripAnnotations,
415+
"merge-objects": mergeObjects,
406416

407417
// TypeScript
408418
"ts-declaration": tsDeclaration,
@@ -464,6 +474,7 @@ if ( !ensureType< FromTsOptions[ 'nonExported' ] >(
464474
) )
465475
throw new Error( );
466476

477+
const mergeObjectsDefault = toType === 'jsc' || toType === 'oapi';
467478

468479
if ( !ensureType<
469480
'ignore' | 'hoist' | 'dot' | 'underscore' | 'reconstruct-all'
@@ -578,6 +589,7 @@ const getWriter = ( ): Writer =>
578589
!doStripAnnotations ? { } :
579590
{ map: ( node => stripAnnotations( node ) ) }
580591
),
592+
mergeObjects: mergeObjects ?? mergeObjectsDefault,
581593
cwd,
582594
shortcut,
583595
}

lib/converter.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,14 @@ export interface ConvertOptions
8282
*/
8383
simplify?: boolean;
8484

85+
/**
86+
* When simplifying (which is implied by this option), and-types of objects
87+
* will be merged into one self-contained object.
88+
* This is useful for type systems that don't treat object intersections the
89+
* same way e.g. TypeScript does.
90+
*/
91+
mergeObjects?: boolean;
92+
8593
/**
8694
* Custom map function for transforming each type after it has been
8795
* converted *from* the source type (and after it has been simplified),
@@ -303,7 +311,7 @@ export function makeConverter(
303311
const simplifiedDoc =
304312
options?.simplify === false
305313
? doc
306-
: simplify( doc );
314+
: simplify( doc, { mergeObjects: options?.mergeObjects } );
307315

308316
const { map, filter, transform } = options ?? { };
309317

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`ts-to-json-schema interface heritage 1`] = `
4+
"{
5+
"definitions": {
6+
"Foo": {
7+
"type": "object",
8+
"properties": {
9+
"b": {
10+
"type": "string",
11+
"title": "Bar.b"
12+
},
13+
"f": {
14+
"type": "number",
15+
"title": "Foo.f"
16+
}
17+
},
18+
"required": [
19+
"b",
20+
"f"
21+
],
22+
"additionalProperties": false,
23+
"title": "Foo, Bar"
24+
},
25+
"Bar": {
26+
"type": "object",
27+
"properties": {
28+
"b": {
29+
"type": "string",
30+
"title": "Bar.b"
31+
}
32+
},
33+
"required": [
34+
"b"
35+
],
36+
"additionalProperties": false,
37+
"title": "Bar"
38+
}
39+
},
40+
"$comment": "Generated by core-types-json-schema (https://github.com/grantila/core-types-json-schema) on behalf of typeconv (https://github.com/grantila/typeconv)"
41+
}"
42+
`;
43+
44+
exports[`ts-to-json-schema type intersection 1`] = `
45+
"{
46+
"definitions": {
47+
"Foo": {
48+
"type": "object",
49+
"properties": {
50+
"b": {
51+
"type": "string",
52+
"title": "Bar.b"
53+
},
54+
"f": {
55+
"type": "number",
56+
"title": "f"
57+
}
58+
},
59+
"required": [
60+
"b",
61+
"f"
62+
],
63+
"additionalProperties": false,
64+
"title": "Foo, Bar"
65+
},
66+
"Bar": {
67+
"type": "object",
68+
"properties": {
69+
"b": {
70+
"type": "string",
71+
"title": "Bar.b"
72+
}
73+
},
74+
"required": [
75+
"b"
76+
],
77+
"additionalProperties": false,
78+
"title": "Bar"
79+
}
80+
},
81+
"$comment": "Generated by core-types-json-schema (https://github.com/grantila/core-types-json-schema) on behalf of typeconv (https://github.com/grantila/typeconv)"
82+
}"
83+
`;

lib/tests/ts-to-json-schema.test.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { expect, describe, it } from "@jest/globals"
2+
3+
import {
4+
makeConverter,
5+
getTypeScriptReader,
6+
getJsonSchemaWriter,
7+
} from '../index.js'
8+
9+
10+
describe( 'ts-to-json-schema', ( ) =>
11+
{
12+
// https://github.com/grantila/typeconv/issues/15
13+
// https://github.com/grantila/typeconv/issues/35
14+
it( 'interface heritage', async ( ) =>
15+
{
16+
const input = `
17+
interface Bar {
18+
b: string;
19+
}
20+
21+
export interface Foo extends Bar {
22+
f: number;
23+
}
24+
`;
25+
26+
const { convert } = makeConverter(
27+
getTypeScriptReader( ),
28+
getJsonSchemaWriter( ),
29+
{
30+
mergeObjects: true,
31+
}
32+
);
33+
34+
const { data } = await convert( { data: input } );
35+
36+
expect( data ).toMatchSnapshot( );
37+
} );
38+
39+
// https://github.com/grantila/typeconv/issues/35
40+
it( 'type intersection', async ( ) =>
41+
{
42+
const input = `
43+
type Bar = {
44+
b: string;
45+
}
46+
47+
export type Foo = Bar & {
48+
f: number;
49+
}
50+
`;
51+
52+
const { convert } = makeConverter(
53+
getTypeScriptReader( ),
54+
getJsonSchemaWriter( ),
55+
{
56+
mergeObjects: true,
57+
}
58+
);
59+
60+
const { data } = await convert( { data: input } );
61+
62+
expect( data ).toMatchSnapshot( );
63+
} );
64+
} );

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,11 @@
6262
"already": "^3.4.1",
6363
"awesome-code-frame": "^1.1.0",
6464
"chalk": "^5.2.0",
65-
"core-types": "^3.0.0",
65+
"core-types": "^3.1.0",
6666
"core-types-graphql": "^3.0.0",
6767
"core-types-json-schema": "^2.1.0",
6868
"core-types-suretype": "^3.2.0",
69-
"core-types-ts": "^4.0.1",
69+
"core-types-ts": "^4.1.0",
7070
"globby": "^13.1.3",
7171
"js-yaml": "^4.1.0",
7272
"oppa": "^0.4.0",

0 commit comments

Comments
 (0)