Skip to content

Commit 8687f6f

Browse files
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
1 parent 0c31459 commit 8687f6f

3 files changed

Lines changed: 75 additions & 0 deletions

File tree

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,13 @@ public class TypeScriptAxiosClientCodegen extends AbstractTypeScriptClientCodege
5353
public static final String AXIOS_VERSION = "axiosVersion";
5454
public static final String DEFAULT_AXIOS_VERSION = "^1.13.5";
5555
public static final String WITH_AWSV4_SIGNATURE = "withAWSV4Signature";
56+
public static final String USE_ERASABLE_SYNTAX = "useErasableSyntax";
57+
public static final String USE_ERASABLE_SYNTAX_DESC = "Use erasable syntax for the generated code, compatible with TypeScript's erasableSyntaxOnly option.";
5658

5759
@Getter @Setter
5860
protected String npmRepository = null;
5961
protected Boolean stringEnums = false;
62+
protected Boolean useErasableSyntax = false;
6063
protected String importFileExtension = "";
6164

6265
@Getter @Setter
@@ -97,6 +100,7 @@ public TypeScriptAxiosClientCodegen() {
97100
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()));
98101
this.cliOptions.add(new CliOption(AXIOS_VERSION, "Use this property to override the axios version in package.json").defaultValue(DEFAULT_AXIOS_VERSION));
99102
this.cliOptions.add(new CliOption(WITH_AWSV4_SIGNATURE, "whether to include AWS v4 signature support", SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));
103+
this.cliOptions.add(new CliOption(USE_ERASABLE_SYNTAX, USE_ERASABLE_SYNTAX_DESC, SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));
100104
// Templates have no mapping between formatted property names and original base names so use only "original" and remove this option
101105
removeOption(CodegenConstants.MODEL_PROPERTY_NAMING);
102106
}
@@ -177,6 +181,11 @@ public void processOpts() {
177181
additionalProperties.put("importFileExtension", this.importFileExtension);
178182
}
179183

184+
if (additionalProperties.containsKey(USE_ERASABLE_SYNTAX)) {
185+
this.useErasableSyntax = Boolean.parseBoolean(additionalProperties.get(USE_ERASABLE_SYNTAX).toString());
186+
additionalProperties.put(USE_ERASABLE_SYNTAX, this.useErasableSyntax);
187+
}
188+
180189
if (additionalProperties.containsKey(NPM_NAME)) {
181190
addNpmPackageGeneration();
182191
}

modules/openapi-generator/src/main/resources/typescript-axios/baseApi.mustache

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,45 @@ export interface RequestArgs {
2525

2626
export class BaseAPI {
2727
protected configuration: Configuration | undefined;
28+
{{^useErasableSyntax}}
2829

2930
constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) {
3031
if (configuration) {
3132
this.configuration = configuration;
3233
this.basePath = configuration.basePath ?? basePath;
3334
}
3435
}
36+
{{/useErasableSyntax}}
37+
{{#useErasableSyntax}}
38+
protected basePath: string;
39+
protected axios: AxiosInstance;
40+
41+
constructor(configuration?: Configuration, basePath: string = BASE_PATH, axios: AxiosInstance = globalAxios) {
42+
this.basePath = basePath;
43+
this.axios = axios;
44+
if (configuration) {
45+
this.configuration = configuration;
46+
this.basePath = configuration.basePath ?? basePath;
47+
}
48+
}
49+
{{/useErasableSyntax}}
3550
};
3651

3752
export class RequiredError extends Error {
53+
{{^useErasableSyntax}}
3854
constructor(public field: string, msg?: string) {
3955
super(msg);
4056
this.name = "RequiredError"
4157
}
58+
{{/useErasableSyntax}}
59+
{{#useErasableSyntax}}
60+
public field: string;
61+
constructor(field: string, msg?: string) {
62+
super(msg);
63+
this.name = "RequiredError"
64+
this.field = field;
65+
}
66+
{{/useErasableSyntax}}
4267
}
4368

4469
interface ServerMap {

modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/axios/TypeScriptAxiosClientCodegenTest.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.testng.annotations.Test;
1212

1313
import java.io.File;
14+
import java.io.IOException;
1415
import java.nio.file.Files;
1516
import java.nio.file.Path;
1617
import java.nio.file.Paths;
@@ -178,4 +179,44 @@ public void testDeprecatedArrayAttribute() throws Exception {
178179
// Verify the non-deprecated array property 'nicknames' is also present
179180
TestUtils.assertFileContains(file, "'nicknames'?: Array<string>");
180181
}
182+
183+
@Test(description = "Verify useErasableSyntax generates erasable code in base.ts")
184+
public void testUseErasableSyntaxConfig() throws IOException {
185+
boolean[] options = {true, false};
186+
for (boolean useErasableSyntax : options) {
187+
final File output = Files.createTempDirectory("typescript_axios_erasable_").toFile();
188+
output.deleteOnExit();
189+
190+
final CodegenConfigurator configurator = new CodegenConfigurator()
191+
.setGeneratorName("typescript-axios")
192+
.setInputSpec("src/test/resources/3_0/petstore.yaml")
193+
.addAdditionalProperty("useErasableSyntax", useErasableSyntax)
194+
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
195+
196+
final ClientOptInput clientOptInput = configurator.toClientOptInput();
197+
final DefaultGenerator generator = new DefaultGenerator();
198+
final List<File> files = generator.opts(clientOptInput).generate();
199+
files.forEach(File::deleteOnExit);
200+
201+
Path baseTsPath = Paths.get(output + "/base.ts");
202+
TestUtils.assertFileExists(baseTsPath);
203+
if (useErasableSyntax) {
204+
// Erasable syntax: no parameter properties, explicit field declarations and assignments
205+
TestUtils.assertFileContains(baseTsPath, "protected basePath: string;");
206+
TestUtils.assertFileContains(baseTsPath, "protected axios: AxiosInstance;");
207+
TestUtils.assertFileContains(baseTsPath, "this.basePath = basePath;");
208+
TestUtils.assertFileContains(baseTsPath, "this.axios = axios;");
209+
TestUtils.assertFileContains(baseTsPath, "public field: string;");
210+
TestUtils.assertFileContains(baseTsPath, "this.field = field;");
211+
// Should NOT contain parameter properties
212+
TestUtils.assertFileNotContains(baseTsPath, "protected basePath: string = BASE_PATH,");
213+
TestUtils.assertFileNotContains(baseTsPath, "public field: string,");
214+
} else {
215+
// Non-erasable syntax: uses parameter properties
216+
TestUtils.assertFileContains(baseTsPath, "protected basePath: string = BASE_PATH,");
217+
TestUtils.assertFileContains(baseTsPath, "protected axios: AxiosInstance = globalAxios");
218+
TestUtils.assertFileContains(baseTsPath, "constructor(public field: string,");
219+
}
220+
}
221+
}
181222
}

0 commit comments

Comments
 (0)