From 24291b196e57b67091091215c3b58504787b8b42 Mon Sep 17 00:00:00 2001 From: Kejia Liu Date: Sat, 1 Aug 2026 13:52:20 +0800 Subject: [PATCH] fix(typescript-fetch): qualify anyToJSON for any-typed form parameters A multipart/form-data parameter with no type at all ("any type") made the generated apis/*.ts call a bare anyToJSON(...). The file only imports the runtime as `* as runtime`, so that identifier does not exist and the output does not compile (TS2304: Cannot find name 'anyToJSON'). formParams.append('meta', new Blob([JSON.stringify(anyToJSON(...))], ...)); The free-form object case already uses runtime.anyToJSON; the any-type case fell through to {{dataType}}ToJSON, and dataType is `any` for an any-typed parameter, so it rendered as `anyToJSON`. Add an isAnyType branch that uses runtime.anyToJSON as well. Regenerating all 787 sample configs produces no diff. --- .../resources/typescript-fetch/apis.mustache | 2 +- .../TypeScriptFetchClientCodegenTest.java | 26 +++++++++++++++++++ .../typescript-fetch/any-type-form-param.yaml | 23 ++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 modules/openapi-generator/src/test/resources/3_0/typescript-fetch/any-type-form-param.yaml diff --git a/modules/openapi-generator/src/main/resources/typescript-fetch/apis.mustache b/modules/openapi-generator/src/main/resources/typescript-fetch/apis.mustache index 97fb15df5253..bb507887b6da 100644 --- a/modules/openapi-generator/src/main/resources/typescript-fetch/apis.mustache +++ b/modules/openapi-generator/src/main/resources/typescript-fetch/apis.mustache @@ -285,7 +285,7 @@ export class {{classname}} extends runtime.BaseAPI { {{^isEnumRef}} {{^withoutRuntimeChecks}} {{^isContainer}} - formParams.append('{{baseName}}', new Blob([JSON.stringify({{#isFreeFormObject}}runtime.anyToJSON{{/isFreeFormObject}}{{^isFreeFormObject}}{{{dataType}}}ToJSON{{/isFreeFormObject}}(requestParameters['{{paramName}}']))], { type: "application/json", })); + formParams.append('{{baseName}}', new Blob([JSON.stringify({{#isFreeFormObject}}runtime.anyToJSON{{/isFreeFormObject}}{{^isFreeFormObject}}{{#isAnyType}}runtime.anyToJSON{{/isAnyType}}{{^isAnyType}}{{{dataType}}}ToJSON{{/isAnyType}}{{/isFreeFormObject}}(requestParameters['{{paramName}}']))], { type: "application/json", })); {{/isContainer}} {{#isContainer}} formParams.append('{{baseName}}', new Blob([JSON.stringify(requestParameters['{{paramName}}'])], { type: "application/json", })); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchClientCodegenTest.java index 0ba3cd8a5305..2aa5894e8c30 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchClientCodegenTest.java @@ -67,6 +67,32 @@ public void testOptionalResponseImports() { Assert.assertEquals(operation.isResponseOptional, true); } + @Test + public void testAnyTypeFormParamUsesQualifiedAnyToJSON() throws IOException { + // A form parameter with no type at all is "any type", not a free-form object. dataType is + // `any`, so `{{dataType}}ToJSON` renders as a bare `anyToJSON` -- but apis.ts only imports + // the runtime as `* as runtime`, so that identifier does not exist and the output does not + // compile. anyToJSON has to be referenced through the runtime namespace, the same way the + // free-form object case already does. + final String specPath = "src/test/resources/3_0/typescript-fetch/any-type-form-param.yaml"; + + File output = Files.createTempDirectory("test").toFile(); + output.deleteOnExit(); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("typescript-fetch") + .setInputSpec(specPath) + .setOutputDir(output.getAbsolutePath().replace("\\", "/")); + + Generator generator = new DefaultGenerator(); + List files = generator.opts(configurator.toClientOptInput()).generate(); + files.forEach(File::deleteOnExit); + + Path api = Paths.get(output + "/apis/DefaultApi.ts"); + TestUtils.assertFileContains(api, "runtime.anyToJSON(requestParameters['meta'])"); + TestUtils.assertFileNotContains(api, "JSON.stringify(anyToJSON("); + } + @Test public void testModelsWithoutPaths() throws IOException { final String specPath = "src/test/resources/3_1/reusable-components-without-paths.yaml"; diff --git a/modules/openapi-generator/src/test/resources/3_0/typescript-fetch/any-type-form-param.yaml b/modules/openapi-generator/src/test/resources/3_0/typescript-fetch/any-type-form-param.yaml new file mode 100644 index 000000000000..ff36b49a5571 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/typescript-fetch/any-type-form-param.yaml @@ -0,0 +1,23 @@ +openapi: 3.0.3 +info: + title: any-typed multipart form parameter + version: 1.0.0 +paths: + /upload: + post: + operationId: upload + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + file: + type: string + format: binary + # no type at all -> "any type", not a free-form object + meta: + description: can be any type + responses: + "200": + description: ok