From e1fc92cef2f09452bbe45eccfa4ca76a0ef6ec74 Mon Sep 17 00:00:00 2001 From: Sergey Fetiskin Date: Thu, 20 Nov 2025 12:21:26 +0100 Subject: [PATCH 1/2] fixes parents when schema has additional properties --- .../src/main/resources/typescript-node/model.mustache | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/typescript-node/model.mustache b/modules/openapi-generator/src/main/resources/typescript-node/model.mustache index 8bd764134687..5b3142076c38 100644 --- a/modules/openapi-generator/src/main/resources/typescript-node/model.mustache +++ b/modules/openapi-generator/src/main/resources/typescript-node/model.mustache @@ -12,7 +12,7 @@ import { {{classname}} } from '{{filename}}'; */ {{/description}} {{^isEnum}} -export class {{classname}} {{#parent}}extends {{{.}}} {{/parent}}{ +export class {{classname}} {{#allParents}}{{#-first}} extends {{/-first}}{{{.}}}{{^-last}}, {{/-last}}{{/allParents}}{ {{#vars}} {{#description}} /** @@ -53,12 +53,12 @@ export class {{classname}} {{#parent}}extends {{{.}}} {{/parent}}{ ]; static getAttributeTypeMap() { - {{#parent}} + {{#allParents}} return super.getAttributeTypeMap().concat({{classname}}.attributeTypeMap); - {{/parent}} - {{^parent}} + {{/allParents}} + {{^allParents}} return {{classname}}.attributeTypeMap; - {{/parent}} + {{/allParents}} } {{/isArray}} } From 61a5d277aadd85a5f9199b3179e48e7d25f8dc7e Mon Sep 17 00:00:00 2001 From: Sergey Fetiskin Date: Thu, 20 Nov 2025 15:24:42 +0100 Subject: [PATCH 2/2] exclude primitive types from parent property --- .../TypeScriptNodeClientCodegen.java | 34 ++++++++++ .../resources/typescript-node/model.mustache | 10 +-- .../TypeScriptNodeModelTest.java | 31 +++++++++ .../3_0/model_with_additional_properties.yaml | 65 +++++++++++++++++++ 4 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 modules/openapi-generator/src/test/resources/3_0/model_with_additional_properties.yaml diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNodeClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNodeClientCodegen.java index 38a512afc947..251e4f25140a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNodeClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptNodeClientCodegen.java @@ -184,6 +184,11 @@ public Map postProcessAllModels(Map objs) for (ModelMap mo : entry.getModels()) { CodegenModel cm = mo.getModel(); + // Filter out primitive types from parent property + if (cm.parent != null && isPrimitiveType(cm.parent)) { + cm.parent = null; + } + // Add additional filename information for imports mo.put("tsImports", toTsImports(cm, cm.imports)); } @@ -289,6 +294,26 @@ private boolean isLanguagePrimitive(String type) { return languageSpecificPrimitives.contains(type); } + /** + * Check if a type is a primitive TypeScript type (string, boolean, number). + * This is used to filter out primitive types from the parent property. + * + * @param type the type to check + * @return true if the type is a primitive type + */ + private boolean isPrimitiveType(String type) { + if (type == null) { + return false; + } + // Check for primitive types (case-insensitive) + String lowerType = type.toLowerCase(Locale.ROOT); + return "string".equals(lowerType) || + "boolean".equals(lowerType) || + "number".equals(lowerType) || + "any".equals(lowerType) || + "array".equals(lowerType); + } + // Determines if the given type is a generic/templated type (ie. ArrayList) private boolean isLanguageGenericType(String type) { for (String genericType : languageGenericTypes) { @@ -323,6 +348,15 @@ private String removeModelPrefixSuffix(String name) { return result; } + @Override + protected void addParentFromContainer(CodegenModel model, Schema schema) { + super.addParentFromContainer(model, schema); + // Filter out primitive types from parent property + if (model.parent != null && isPrimitiveType(model.parent)) { + model.parent = null; + } + } + @Override protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel, Schema schema) { super.addAdditionPropertiesToCodeGenModel(codegenModel, schema); diff --git a/modules/openapi-generator/src/main/resources/typescript-node/model.mustache b/modules/openapi-generator/src/main/resources/typescript-node/model.mustache index 5b3142076c38..8bd764134687 100644 --- a/modules/openapi-generator/src/main/resources/typescript-node/model.mustache +++ b/modules/openapi-generator/src/main/resources/typescript-node/model.mustache @@ -12,7 +12,7 @@ import { {{classname}} } from '{{filename}}'; */ {{/description}} {{^isEnum}} -export class {{classname}} {{#allParents}}{{#-first}} extends {{/-first}}{{{.}}}{{^-last}}, {{/-last}}{{/allParents}}{ +export class {{classname}} {{#parent}}extends {{{.}}} {{/parent}}{ {{#vars}} {{#description}} /** @@ -53,12 +53,12 @@ export class {{classname}} {{#allParents}}{{#-first}} extends {{/-first}}{{{.}}} ]; static getAttributeTypeMap() { - {{#allParents}} + {{#parent}} return super.getAttributeTypeMap().concat({{classname}}.attributeTypeMap); - {{/allParents}} - {{^allParents}} + {{/parent}} + {{^parent}} return {{classname}}.attributeTypeMap; - {{/allParents}} + {{/parent}} } {{/isArray}} } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/typescriptnode/TypeScriptNodeModelTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/typescriptnode/TypeScriptNodeModelTest.java index 23e342f4bc27..73da32e0f435 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/typescriptnode/TypeScriptNodeModelTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/typescriptnode/TypeScriptNodeModelTest.java @@ -354,4 +354,35 @@ public void mappedFromModelTest() { Assert.assertEquals(cm.name, "ApiResponse"); Assert.assertEquals(cm.classFilename, mappedName); } + + @Test(description = "should exclude TypeScript primitive types from parent property") + public void modelShouldExcludePrimitiveTypesFromParentPropertyTest() { + final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/model_with_additional_properties.yaml"); + final DefaultCodegen codegen = new TypeScriptNodeClientCodegen(); + codegen.setOpenAPI(openAPI); + + // Check that all models with primitive additionalProperties don't have parent property set + for (String modelName : openAPI.getComponents().getSchemas().keySet()) { + final Schema schema = openAPI.getComponents().getSchemas().get(modelName); + final CodegenModel model = codegen.fromModel(modelName, schema); + Assert.assertNull(model.parent, "Model " + modelName + " should not have a parent property"); + } + } + + @Test(description = "should extend from parent") + public void shouldExtendFromParentTest() { + final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/allOf.yaml"); + final DefaultCodegen codegen = new TypeScriptNodeClientCodegen(); + codegen.setOpenAPI(openAPI); + + // Check that Child model extends Person + final Schema childSchema = openAPI.getComponents().getSchemas().get("Child"); + final CodegenModel childModel = codegen.fromModel("Child", childSchema); + Assert.assertEquals(childModel.parent, "Person"); + + // Check that Adult model extends Person + final Schema adultSchema = openAPI.getComponents().getSchemas().get("Adult"); + final CodegenModel adultModel = codegen.fromModel("Adult", adultSchema); + Assert.assertEquals(adultModel.parent, "Person"); + } } diff --git a/modules/openapi-generator/src/test/resources/3_0/model_with_additional_properties.yaml b/modules/openapi-generator/src/test/resources/3_0/model_with_additional_properties.yaml new file mode 100644 index 000000000000..c2a6e64e30e6 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/model_with_additional_properties.yaml @@ -0,0 +1,65 @@ +openapi: 3.0.0 +info: + version: 1.0.0 + title: OpenAPI Petstore + license: + name: Apache-2.0 + url: 'https://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /pet: + get: + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' +components: + schemas: + Pet: + title: a Pet with string additionalProperties + type: object + properties: + id: + type: integer + format: int64 + additionalProperties: + type: string + PetBoolean: + title: a Pet with boolean additionalProperties + type: object + properties: + id: + type: integer + format: int64 + additionalProperties: + type: boolean + PetNumber: + title: a Pet with number additionalProperties + type: object + properties: + id: + type: integer + format: int64 + additionalProperties: + type: number + PetAny: + title: a Pet with any additionalProperties + type: object + properties: + id: + type: integer + format: int64 + additionalProperties: true + PetArray: + title: a Pet with array additionalProperties + type: object + properties: + id: + type: integer + format: int64 + additionalProperties: + type: array + items: + type: string