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

JU
9h ago
+472-6
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.