Skip to content

Commit 75b71f7

Browse files
authored
chore: Add support for refs with property path (#309)
1 parent ac7027b commit 75b71f7

File tree

2 files changed

+68
-9
lines changed

2 files changed

+68
-9
lines changed

plugins/typescript/src/core/schemaToTypeAliasDeclaration.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,49 @@ describe("schemaToTypeAliasDeclaration", () => {
8989
`);
9090
});
9191

92+
it("should resolve ref in object properties to deep linking", () => {
93+
const schema: SchemaObject = {
94+
type: "object",
95+
properties: {
96+
age: { $ref: "#/components/schemas/Age/properties/age" },
97+
breed: { $ref: "#/components/schemas/Breeds/items/properties/name" },
98+
color: {
99+
$ref: "#/components/schemas/Breeds/items/properties/color/items",
100+
},
101+
},
102+
required: ["age", "breed"],
103+
};
104+
105+
expect(
106+
printSchema(schema, "Dog", "schemas", {
107+
schemas: {
108+
Breeds: {
109+
type: "array",
110+
items: {
111+
type: "object",
112+
properties: {
113+
name: { type: "string" },
114+
color: { type: "array", items: { type: "string" } },
115+
},
116+
},
117+
},
118+
Age: {
119+
type: "object",
120+
properties: {
121+
age: { type: "integer" },
122+
},
123+
},
124+
},
125+
})
126+
).toMatchInlineSnapshot(`
127+
"export type Dog = {
128+
age: Age["age"];
129+
breed: Breeds[number]["name"];
130+
color?: Breeds[number]["color"][number];
131+
};"
132+
`);
133+
});
134+
92135
it("should generate enums (strings)", () => {
93136
const schema: SchemaObject = {
94137
type: "string",

plugins/typescript/src/core/schemaToTypeAliasDeclaration.ts

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,22 +79,38 @@ export const getType = (
7979
isNodeEnum?: boolean
8080
): ts.TypeNode => {
8181
if (isReferenceObject(schema)) {
82-
const [hash, topLevel, namespace, name] = schema.$ref.split("/");
82+
const [hash, topLevel, namespace, name, ...propertyPath] =
83+
schema.$ref.split("/");
8384
if (hash !== "#" || topLevel !== "components") {
8485
throw new Error(
8586
"This library only resolve $ref that are include into `#/components/*` for now"
8687
);
8788
}
88-
if (namespace === context.currentComponent) {
89-
return f.createTypeReferenceNode(f.createIdentifier(pascal(name)));
90-
}
9189

92-
return f.createTypeReferenceNode(
93-
f.createQualifiedName(
94-
f.createIdentifier(pascal(namespace)),
95-
f.createIdentifier(pascal(name))
96-
)
90+
let refNode: ts.TypeNode = f.createTypeReferenceNode(
91+
namespace === context.currentComponent
92+
? f.createIdentifier(pascal(name))
93+
: f.createQualifiedName(
94+
f.createIdentifier(pascal(namespace)),
95+
f.createIdentifier(pascal(name))
96+
)
9797
);
98+
99+
for (let i = 0; i < propertyPath.length; i++) {
100+
if (propertyPath[i] === "properties" && propertyPath[i + 1]) {
101+
refNode = f.createIndexedAccessTypeNode(
102+
refNode,
103+
f.createLiteralTypeNode(f.createStringLiteral(propertyPath[++i]))
104+
);
105+
} else if (propertyPath[i] === "items") {
106+
refNode = f.createIndexedAccessTypeNode(
107+
refNode,
108+
f.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)
109+
);
110+
}
111+
}
112+
113+
return refNode;
98114
}
99115

100116
if (schema["x-openapi-codegen"]?.type === "never") {

0 commit comments

Comments
 (0)