Skip to content

Commit c2aaa11

Browse files
committed
fix(typescript-fetch): filter Null type from oneOf to prevent missing model references
In OpenAPI 3.1, oneOf can include type: 'null' to represent nullable unions. The codegen maps this to a "Null" model name and adds it to the oneOf variants list and imports. However, no Null.ts model file is generated, causing TypeScript import errors like: Module '"../models/index"' has no exported member 'Null' Fix: remove "Null" from cm.oneOf and filter it from oneOfModels during model post-processing, following the same pattern used by the C#, Java, and PowerShell generators. The model is marked as nullable instead.
1 parent cca5dda commit c2aaa11

3 files changed

Lines changed: 79 additions & 0 deletions

File tree

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,12 +813,21 @@ private ExtendedCodegenModel processCodeGenModel(ExtendedCodegenModel cm) {
813813
.map(CodegenComposedSchemas::getOneOf)
814814
.orElse(Collections.emptyList());
815815

816+
// Remove "Null" from oneOf variants. In OpenAPI 3.1, oneOf can include
817+
// `type: 'null'` to represent nullable types. The codegen maps this to a
818+
// "Null" model name, but no Null model file is generated, causing import
819+
// errors. Instead, mark the model as nullable and filter out the Null entry.
820+
if (cm.oneOf != null && !cm.oneOf.isEmpty() && cm.oneOf.remove("Null")) {
821+
cm.isNullable = true;
822+
}
823+
816824
// create a set of any non-primitive, non-array types used in the oneOf schemas which will
817825
// need to be imported.
818826
cm.oneOfModels = oneOfsList.stream()
819827
.filter(cp -> !cp.getIsPrimitiveType() && !cp.getIsArray())
820828
.map(CodegenProperty::getBaseType)
821829
.filter(Objects::nonNull)
830+
.filter(baseType -> !"Null".equals(baseType))
822831
.collect(Collectors.toCollection(TreeSet::new));
823832

824833
// create a set of any complex, inner types used by arrays in the oneOf schema (e.g. if

modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchClientCodegenTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,31 @@ public void testOneOfModelsImportNonPrimitiveTypes() throws IOException {
444444
TestUtils.assertFileContains(testResponse, "import type { OptionThree } from './OptionThree'");
445445
}
446446

447+
@Test(description = "Verify nullable oneOf does not generate Null model references")
448+
public void testNullableOneOfDoesNotImportNullModel() throws IOException {
449+
File output = generate(
450+
Collections.emptyMap(),
451+
"src/test/resources/3_0/typescript-fetch/nullable-oneOf.yaml"
452+
);
453+
454+
Path nullableResult = Paths.get(output + "/models/NullableResult.ts");
455+
TestUtils.assertFileExists(nullableResult);
456+
457+
// Should not import or reference a non-existent "Null" model
458+
TestUtils.assertFileNotContains(nullableResult, "import type { Null }");
459+
TestUtils.assertFileNotContains(nullableResult, "NullFromJSON");
460+
TestUtils.assertFileNotContains(nullableResult, "NullToJSON");
461+
TestUtils.assertFileNotContains(nullableResult, "instanceOfNull");
462+
// Should contain the valid model types
463+
TestUtils.assertFileContains(nullableResult, "FileLocation");
464+
TestUtils.assertFileContains(nullableResult, "DetailedLocation");
465+
// Union type should not include Null
466+
TestUtils.assertFileContains(nullableResult, "export type NullableResult = DetailedLocation | FileLocation");
467+
468+
// No Null.ts model file should be generated
469+
TestUtils.assertFileNotExists(Paths.get(output + "/models/Null.ts"));
470+
}
471+
447472
@Test(description = "Verify validationAttributes works with withoutRuntimeChecks=true")
448473
public void testValidationAttributesWithWithoutRuntimeChecks() throws IOException {
449474
Map<String, Object> properties = new HashMap<>();
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
openapi: 3.1.0
2+
info:
3+
version: 1.0.0
4+
title: testing nullable oneOf (Null type filtering)
5+
paths:
6+
/locations/{locationId}:
7+
get:
8+
operationId: getLocation
9+
parameters:
10+
- name: locationId
11+
in: path
12+
required: true
13+
schema:
14+
type: string
15+
responses:
16+
200:
17+
description: OK
18+
content:
19+
application/json:
20+
schema:
21+
$ref: '#/components/schemas/NullableResult'
22+
components:
23+
schemas:
24+
NullableResult:
25+
oneOf:
26+
- $ref: '#/components/schemas/FileLocation'
27+
- $ref: '#/components/schemas/DetailedLocation'
28+
- type: 'null'
29+
FileLocation:
30+
type: object
31+
properties:
32+
path:
33+
type: string
34+
required:
35+
- path
36+
DetailedLocation:
37+
type: object
38+
properties:
39+
path:
40+
type: string
41+
size:
42+
type: integer
43+
required:
44+
- path
45+
- size

0 commit comments

Comments
 (0)