Skip to content

Commit a65e856

Browse files
committed
implement inferred type naming
1 parent 3bbff8e commit a65e856

File tree

3 files changed

+93
-3
lines changed

3 files changed

+93
-3
lines changed

src/metadata/resolveType.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { EnumDeclaration, Expression, Type as MorphType, Node, Project, ts } from "ts-morph";
1+
import { EnumDeclaration, Expression, Type as MorphType, Node, Project, ts, TypeNode } from "ts-morph";
22
import { getJSDocDescriptionFromProperty } from "../utils/jsDocUtils";
33
import { getNodeAsTsMorphNode } from "../utils/utils";
44
import {
@@ -142,6 +142,13 @@ export function resolveType(type?: MorphType, node?: Node): Type {
142142
}
143143
}
144144

145+
if (Node.isTypeNode(node)) {
146+
typeNodeName = node.getText() ?? "";
147+
if (typeNodeName.trim().startsWith("{")) {
148+
typeNodeName = "";
149+
}
150+
}
151+
145152
typeName = typeName || typeNodeName;
146153

147154
typeName = replaceNameText(typeName);
@@ -256,9 +263,17 @@ export function resolveType(type?: MorphType, node?: Node): Type {
256263
};
257264
return enumType;
258265
}
266+
let unionTypeNodes: TypeNode[] = [];
267+
if (Node.isTyped(node)) {
268+
let typeNode = node?.getTypeNode();
259269

260-
const unionTypes = type.getUnionTypes().map((subType) => {
261-
return resolveType(subType);
270+
if (Node.isUnionTypeNode(typeNode)) {
271+
unionTypeNodes = typeNode.getTypeNodes();
272+
}
273+
}
274+
275+
const unionTypes = type.getUnionTypes().map((subType, index) => {
276+
return resolveType(subType, unionTypeNodes[index]);
262277
});
263278

264279
// Remove duplicate types from union

test/data/apis.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,40 @@ export class TestUnionType {
3030
}
3131
}
3232

33+
type inferred<T> = T extends {
34+
a: {
35+
type: any;
36+
};
37+
}
38+
? T["a"]["type"]
39+
: never;
40+
41+
const typeToInferA = {
42+
a: {
43+
type: {
44+
a: 123,
45+
b: "string"
46+
}
47+
}
48+
};
49+
50+
type inferredTypeA = inferred<typeof typeToInferA>;
51+
52+
const typeToInferB = {
53+
a: {
54+
type: {
55+
a: "string",
56+
b: 123
57+
}
58+
}
59+
};
60+
61+
type inferredTypeB = inferred<typeof typeToInferB>;
62+
63+
type ObjectWithInferredTypeUnion = {
64+
data: inferredTypeB | inferredTypeA;
65+
};
66+
3367
interface Address {
3468
street: string;
3569
}
@@ -238,6 +272,18 @@ export class MyService {
238272
public interfaceWithFunctions(body: InterfaceWithFunctions): any {
239273
return body;
240274
}
275+
276+
@POST
277+
@Path("zod-type")
278+
public zodType(body: inferredTypeB): any {
279+
return body;
280+
}
281+
282+
@POST
283+
@Path("zod-union-type")
284+
public objectWithZodTypeUnion(body: ObjectWithInferredTypeUnion): any {
285+
return body;
286+
}
241287
}
242288

243289
class BaseService {

test/unit/definitions.spec.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,4 +574,33 @@ describe("Definition generation", () => {
574574
expect(Object.keys(typeSpec.properties)).toEqual([]);
575575
});
576576
});
577+
578+
describe("Inferred types should be named properly", () => {
579+
test("Inferred types should have the right name and definition", async () => {
580+
let expression = jsonata("components.schemas.inferredTypeB");
581+
let typeSpec = await expression.evaluate(spec);
582+
expect(typeSpec).toBeDefined();
583+
expect(typeSpec.properties).toBeDefined();
584+
expect(typeSpec.properties.a).toBeDefined();
585+
expect(typeSpec.properties.a.type).toEqual("string");
586+
expect(typeSpec.properties.b).toBeDefined();
587+
expect(typeSpec.properties.b.type).toEqual("number");
588+
});
589+
590+
test("Inferred union types should create named types for each instance", async () => {
591+
let expression = jsonata(
592+
'paths."/mypath/zod-union-type".post.requestBody.content."application/json".schema."$ref"'
593+
);
594+
let reqBodyRef: string = await expression.evaluate(spec);
595+
596+
expect(reqBodyRef).toEqual("#/components/schemas/ObjectWithInferredTypeUnion");
597+
598+
expression = jsonata("components.schemas.ObjectWithInferredTypeUnion.properties.data");
599+
let propSpec = await expression.evaluate(spec);
600+
expect(propSpec.oneOf).toBeDefined();
601+
expect(propSpec.oneOf.length).toEqual(2);
602+
expect(propSpec.oneOf[0].$ref).toEqual("#/components/schemas/inferredTypeB");
603+
expect(propSpec.oneOf[1].$ref).toEqual("#/components/schemas/inferredTypeA");
604+
});
605+
});
577606
});

0 commit comments

Comments
 (0)