From ea60e70b3f81462fbcfa37e156a02e0fbc4b24d6 Mon Sep 17 00:00:00 2001 From: thobed Date: Sat, 14 Mar 2026 09:32:45 -0400 Subject: [PATCH 1/4] fix(typescript-axios): add useErasableSyntax support for erasableSyntaxOnly compatibility Add useErasableSyntax option to the typescript-axios generator so that generated base.ts avoids TypeScript parameter properties (protected/public in constructor params), which are incompatible with TypeScript 5.8's erasableSyntaxOnly flag. Closes #22540 --- .../TypeScriptAxiosClientCodegen.java | 9 ++++ .../typescript-axios/baseApi.mustache | 25 +++++++++++ .../TypeScriptAxiosClientCodegenTest.java | 41 +++++++++++++++++++ 3 files changed, 75 insertions(+) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java index 6898d3d7ccfd..774a38664fac 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java @@ -53,10 +53,13 @@ public class TypeScriptAxiosClientCodegen extends AbstractTypeScriptClientCodege public static final String AXIOS_VERSION = "axiosVersion"; public static final String DEFAULT_AXIOS_VERSION = "^1.13.5"; public static final String WITH_AWSV4_SIGNATURE = "withAWSV4Signature"; + public static final String USE_ERASABLE_SYNTAX = "useErasableSyntax"; + public static final String USE_ERASABLE_SYNTAX_DESC = "Use erasable syntax for the generated code, compatible with TypeScript's erasableSyntaxOnly option."; @Getter @Setter protected String npmRepository = null; protected Boolean stringEnums = false; + protected Boolean useErasableSyntax = false; protected String importFileExtension = ""; @Getter @Setter @@ -97,6 +100,7 @@ public TypeScriptAxiosClientCodegen() { this.cliOptions.add(new CliOption(USE_SQUARE_BRACKETS_IN_ARRAY_NAMES, "Setting this property to true will add brackets to array attribute names, e.g. my_values[].", SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString())); this.cliOptions.add(new CliOption(AXIOS_VERSION, "Use this property to override the axios version in package.json").defaultValue(DEFAULT_AXIOS_VERSION)); this.cliOptions.add(new CliOption(WITH_AWSV4_SIGNATURE, "whether to include AWS v4 signature support", SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString())); + this.cliOptions.add(new CliOption(USE_ERASABLE_SYNTAX, USE_ERASABLE_SYNTAX_DESC, SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString())); // Templates have no mapping between formatted property names and original base names so use only "original" and remove this option removeOption(CodegenConstants.MODEL_PROPERTY_NAMING); } @@ -177,6 +181,11 @@ public void processOpts() { additionalProperties.put("importFileExtension", this.importFileExtension); } + if (additionalProperties.containsKey(USE_ERASABLE_SYNTAX)) { + this.useErasableSyntax = Boolean.parseBoolean(additionalProperties.get(USE_ERASABLE_SYNTAX).toString()); + additionalProperties.put(USE_ERASABLE_SYNTAX, this.useErasableSyntax); + } + if (additionalProperties.containsKey(NPM_NAME)) { addNpmPackageGeneration(); } diff --git a/modules/openapi-generator/src/main/resources/typescript-axios/baseApi.mustache b/modules/openapi-generator/src/main/resources/typescript-axios/baseApi.mustache index 2bfb779f8194..7655a33f0c12 100644 --- a/modules/openapi-generator/src/main/resources/typescript-axios/baseApi.mustache +++ b/modules/openapi-generator/src/main/resources/typescript-axios/baseApi.mustache @@ -25,6 +25,7 @@ export interface RequestArgs { export class BaseAPI { protected configuration: Configuration | undefined; +{{^useErasableSyntax}} constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) { if (configuration) { @@ -32,13 +33,37 @@ export class BaseAPI { this.basePath = configuration.basePath ?? basePath; } } +{{/useErasableSyntax}} +{{#useErasableSyntax}} + protected basePath: string; + protected axios: AxiosInstance; + + constructor(configuration?: Configuration, basePath: string = BASE_PATH, axios: AxiosInstance = globalAxios) { + this.basePath = basePath; + this.axios = axios; + if (configuration) { + this.configuration = configuration; + this.basePath = configuration.basePath ?? basePath; + } + } +{{/useErasableSyntax}} }; export class RequiredError extends Error { +{{^useErasableSyntax}} constructor(public field: string, msg?: string) { super(msg); this.name = "RequiredError" } +{{/useErasableSyntax}} +{{#useErasableSyntax}} + public field: string; + constructor(field: string, msg?: string) { + super(msg); + this.name = "RequiredError" + this.field = field; + } +{{/useErasableSyntax}} } interface ServerMap { diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/axios/TypeScriptAxiosClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/axios/TypeScriptAxiosClientCodegenTest.java index 1713e2ca4a4d..54c408eacbe9 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/axios/TypeScriptAxiosClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/axios/TypeScriptAxiosClientCodegenTest.java @@ -11,6 +11,7 @@ import org.testng.annotations.Test; import java.io.File; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -178,4 +179,44 @@ public void testDeprecatedArrayAttribute() throws Exception { // Verify the non-deprecated array property 'nicknames' is also present TestUtils.assertFileContains(file, "'nicknames'?: Array"); } + + @Test(description = "Verify useErasableSyntax generates erasable code in base.ts") + public void testUseErasableSyntaxConfig() throws IOException { + boolean[] options = {true, false}; + for (boolean useErasableSyntax : options) { + final File output = Files.createTempDirectory("typescript_axios_erasable_").toFile(); + output.deleteOnExit(); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("typescript-axios") + .setInputSpec("src/test/resources/3_0/petstore.yaml") + .addAdditionalProperty("useErasableSyntax", useErasableSyntax) + .setOutputDir(output.getAbsolutePath().replace("\\", "/")); + + final ClientOptInput clientOptInput = configurator.toClientOptInput(); + final DefaultGenerator generator = new DefaultGenerator(); + final List files = generator.opts(clientOptInput).generate(); + files.forEach(File::deleteOnExit); + + Path baseTsPath = Paths.get(output + "/base.ts"); + TestUtils.assertFileExists(baseTsPath); + if (useErasableSyntax) { + // Erasable syntax: no parameter properties, explicit field declarations and assignments + TestUtils.assertFileContains(baseTsPath, "protected basePath: string;"); + TestUtils.assertFileContains(baseTsPath, "protected axios: AxiosInstance;"); + TestUtils.assertFileContains(baseTsPath, "this.basePath = basePath;"); + TestUtils.assertFileContains(baseTsPath, "this.axios = axios;"); + TestUtils.assertFileContains(baseTsPath, "public field: string;"); + TestUtils.assertFileContains(baseTsPath, "this.field = field;"); + // Should NOT contain parameter properties + TestUtils.assertFileNotContains(baseTsPath, "protected basePath: string = BASE_PATH,"); + TestUtils.assertFileNotContains(baseTsPath, "public field: string,"); + } else { + // Non-erasable syntax: uses parameter properties + TestUtils.assertFileContains(baseTsPath, "protected basePath: string = BASE_PATH,"); + TestUtils.assertFileContains(baseTsPath, "protected axios: AxiosInstance = globalAxios"); + TestUtils.assertFileContains(baseTsPath, "constructor(public field: string,"); + } + } + } } From 0417ca00f02d2ff0a1a0a233bb996e895a9b4630 Mon Sep 17 00:00:00 2001 From: thobed Date: Sat, 14 Mar 2026 09:32:46 -0400 Subject: [PATCH 2/4] test: add missing assertion for axios parameter property absence Adds assertFileNotContains check for the axios parameter property in erasable syntax mode to close the regression gap. --- .../typescript/axios/TypeScriptAxiosClientCodegenTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/axios/TypeScriptAxiosClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/axios/TypeScriptAxiosClientCodegenTest.java index 54c408eacbe9..b4fad35d5bd9 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/axios/TypeScriptAxiosClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/axios/TypeScriptAxiosClientCodegenTest.java @@ -210,6 +210,7 @@ public void testUseErasableSyntaxConfig() throws IOException { TestUtils.assertFileContains(baseTsPath, "this.field = field;"); // Should NOT contain parameter properties TestUtils.assertFileNotContains(baseTsPath, "protected basePath: string = BASE_PATH,"); + TestUtils.assertFileNotContains(baseTsPath, "protected axios: AxiosInstance = globalAxios"); TestUtils.assertFileNotContains(baseTsPath, "public field: string,"); } else { // Non-erasable syntax: uses parameter properties From 0acb15c7e5031b8863ef48fb7e54ecf6a3dbe6c3 Mon Sep 17 00:00:00 2001 From: thobed Date: Sat, 14 Mar 2026 09:32:46 -0400 Subject: [PATCH 3/4] fix(typescript-axios): warn when useErasableSyntax and stringEnums are both enabled TypeScript enum declarations are not erasable syntax and will fail with erasableSyntaxOnly. Log a warning guiding users to disable stringEnums (the default generates erasable-compatible const objects). --- .../languages/TypeScriptAxiosClientCodegen.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java index 774a38664fac..d920cc01a5da 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java @@ -22,6 +22,8 @@ import lombok.Getter; import lombok.Setter; import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.openapitools.codegen.*; import org.openapitools.codegen.meta.features.DocumentationFeature; import org.openapitools.codegen.meta.features.SecurityFeature; @@ -39,6 +41,8 @@ public class TypeScriptAxiosClientCodegen extends AbstractTypeScriptClientCodegen { + private static final Logger LOGGER = LoggerFactory.getLogger(TypeScriptAxiosClientCodegen.class); + public static final String NPM_REPOSITORY = "npmRepository"; public static final String WITH_INTERFACES = "withInterfaces"; public static final String SEPARATE_MODELS_AND_API = "withSeparateModelsAndApi"; @@ -186,6 +190,13 @@ public void processOpts() { additionalProperties.put(USE_ERASABLE_SYNTAX, this.useErasableSyntax); } + if (this.useErasableSyntax && this.stringEnums) { + LOGGER.warn("useErasableSyntax and stringEnums are both enabled. " + + "TypeScript 'enum' declarations are not erasable syntax and will fail with " + + "erasableSyntaxOnly. Consider disabling stringEnums (the default generates " + + "erasable-compatible const objects instead)."); + } + if (additionalProperties.containsKey(NPM_NAME)) { addNpmPackageGeneration(); } From 30f7c22825952f0ba272b1d89b4588cbcd9d23cd Mon Sep 17 00:00:00 2001 From: thobed Date: Sat, 14 Mar 2026 09:55:48 -0400 Subject: [PATCH 4/4] fix(typescript-axios): make LOGGER non-static per project ArchUnit rules The project enforces that Logger fields must not be static to avoid unnecessary memory consumption, since generators are used once per program lifetime (see PR #8799). --- .../codegen/languages/TypeScriptAxiosClientCodegen.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java index d920cc01a5da..4c38da4c10c2 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java @@ -41,7 +41,7 @@ public class TypeScriptAxiosClientCodegen extends AbstractTypeScriptClientCodegen { - private static final Logger LOGGER = LoggerFactory.getLogger(TypeScriptAxiosClientCodegen.class); + private final Logger LOGGER = LoggerFactory.getLogger(TypeScriptAxiosClientCodegen.class); public static final String NPM_REPOSITORY = "npmRepository"; public static final String WITH_INTERFACES = "withInterfaces";