Skip to content

fix(typescript-axios): preserve primitive anyOf types and nullability - #25638

Open
Justineo wants to merge 3 commits into
OpenAPITools:masterfrom
Justineo:fix/typed-anyof-nullability
Open

Justineo wants to merge 3 commits into
OpenAPITools:masterfrom
Justineo:fix/typed-anyof-nullability

Conversation

@Justineo

@Justineo Justineo commented Nov 12, 2026 •

Copy link
Copy Markdown
AI disclosure — Codex, GPT-6 Astra (High)

Codex investigated the normalizer and Axios generator, implemented this change, wrote the standalone fixtures and regression tests, ran the local checks below, and drafted this PR. I supplied the problem, requested a separate upstream fix, and asked for alignment with the project's contribution requirements.

The work included reproducing the failure against current master, preserving schema constraints during normalization, checking genuine nullability and nullable references, and compiling and exercising the generated SDK. The fixtures use synthetic schemas.

The local Maven build/tests, sample regeneration, documentation export, and SDK checks below completed. See the current PR checks for upstream CI status. TypeScript aliases preserve the primitive types; they do not perform runtime pattern or range validation.

Description

Closes #25637. This branch starts from master and is independent of #25636.

For a schema such as:

Hostname:
  type: string
  anyOf:
    - pattern: '^[a-z]+[.]example$'
    - pattern: '^[*][.][a-z]+[.]example$'

typescript-axios generates an empty Hostname interface and makes references to it nullable. SDK methods consequently accept numbers and null, and a returned hostname cannot be used as a string.

After this change, the model is export type Hostname = string, and a required non-nullable property is hostname: Hostname. Schemas that explicitly allow null retain it. Numeric validation alternatives likewise retain the enclosing numeric type.

Changes

  • Keep validation-only schemas, enum/const alternatives, format annotations and structural constraints such as contains out of null-schema detection. Reuse the existing metadata-only schema predicate to avoid discarding structural constraints during OpenAPI 3.1 normalization.
  • Preserve the enclosing type and constraints when a single validation-only or annotation-only branch remains. Removing a null alternative must not widen an explicitly typed parent to nullable.
  • Retain untyped validation branches during oneOf cleanup. Axios uses the same primitive-alias path for these oneOf and anyOf schemas, in both combined and separate output, so retaining the predicates does not produce any.
  • Normalize OpenAPI 3.1 nullable type arrays on anyOf parents.
  • Emit primitive aliases for the Axios models, including inline models and the single-member allOf wrappers used for nullable references. The OAS alternatives remain intact.
  • Limit this alias handling to primitive parents with validation-only alternatives. Enum, const, mixed-type and other composed constraints are not replaced by the primitive fallback.

Consumers that relied on the incorrect number/null assignments will now get TypeScript errors. Existing model names are retained.

Validation

  • The initial ModelUtils, normalizer and Axios regression cases fail against unmodified master df804ac9802fba0c51a64f8c3cb03191ed3dde2d. The released 7.27.0 CLI reproduces the same empty-interface and nullable-property output.
  • Regression coverage includes OpenAPI 3.0/3.1, single and multiple alternatives, required/inline properties, numeric ranges, explicit nullability, nullable references, and combined/separate model files. Normalizer assertions check that the original pattern/range constraints remain present, including validation-only oneOf, annotation-only alternatives, contains, inline properties and nullable $ref wrappers. Untyped enum/const alternatives are retained, while genuinely null-only enum alternatives still simplify correctly. The fallback guard test also checks the model's enum values.
  • Strict TypeScript 5.9.3 checks pass in all four version/layout combinations. They cover actual SDK method arguments and responses, reject numbers and inappropriate nulls, and accept genuinely nullable values.
  • Generated SDKs in all four combinations pass request-serialization and response-pass-through checks using an Axios adapter.
  • Both standalone fixtures pass CLI spec validation.
  • Full Maven reactor clean package and final package runs pass with JDK 21 and -Dmaven.javadoc.skip=true. The final run reports 5,897 tests, zero failures/errors and 18 skipped.
  • All 836 sample configurations regenerate successfully, with no changes to existing samples. Exporting all generator documentation also leaves no changes.

PR checklist

  • Read the contribution guidelines.
  • Run the full project ./mvnw clean package -Dmaven.javadoc.skip=true check.
  • Regenerate all sample configurations with ./bin/generate-samples.sh ./bin/configs/*.yaml and verify the diff.
  • Export all generator documentation with ./bin/utils/export_docs_generators.sh and verify the diff.
  • Mention the TypeScript Axios technical committee member: @nicokoenig.

Summary by cubic

Fixes #25637. For schemas with a primitive type and validation-only anyOf/oneOf branches (pattern, range, length, etc.), typescript-axios now preserves the primitive type instead of generating an empty interface. Hostname with type: string and pattern-only branches now generates export type Hostname = string, and required non-nullable properties are typed Hostname rather than nullable. Schemas that explicitly allow null keep it, and numeric validation alternatives keep the enclosing number type. Code that relied on the previous number/null assignments will now get TypeScript errors.

Changes

  • Validation-only, annotation-only, enum, and const branches are no longer treated as null schemas or discarded during normalization, so their constraints and metadata survive.
  • Removing a null alternative no longer widens an explicitly typed parent to nullable.
  • OpenAPI 3.1 nullable type arrays on anyOf parents are normalized to nullable.
  • Primitive aliases are emitted for such models, including inline models and single-member allOf wrappers used for nullable references.
  • Enum, const, mixed-type, and other composed constraints are not replaced by the primitive fallback.

Written for commit a6cd4e4. Summary will update on new commits.

View guided diff

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 10 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/axios/TypeScriptAxiosClientCodegenTest.java">

<violation number="1" location="modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/axios/TypeScriptAxiosClientCodegenTest.java:45">
P3: This test is named for preserving other type constraints, but it only asserts that `x-typescript-primitive-type` is absent. Also assert the model's generated type or enum/const values so the test fails if those schemas are broken.</violation>
</file>

<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java:2295">
P2: `ModelUtils.hasValidation` does not check `contains`, so a type-less schema carrying that constraint still passes this guard and is replaced by an empty schema, dropping the constraint. Include `contains` and the other applicable validation keywords in the check.</violation>
</file>

Reply to a comment to ask cubic a question or push back. It learns from your replies.

View guided diff | Re-trigger cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 9 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

View guided diff | Re-trigger cubic

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG][typescript-axios] Primitive anyOf validation becomes an empty nullable model

1 participant