OpenAPITools / OpenAPITools/openapi-generator

[BUG][typescript-axios] Missing import for a $ref under additionalProperties on a 3.1 nullable property

Open
#24,517 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Java
Stars
26.8k
Forks
7.7k
PR merge metrics
PR metrics pending

Description

Bug Report Checklist
  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator (example)?
  • What's the version of OpenAPI Generator used?
  • Have you search for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Bounty to sponsor the fix
Description

When a property is nullable using an OpenAPI 3.1 type array ("type": ["null", "object"]) and its additionalProperties is a $ref, the generated model references the $ref'd type but never imports it, so the output does not compile.

Instead of importing the referenced model, the generator emits an import for a Null model that it never writes:

// @ts-ignore
import type { Null } from './null';        // <- ./null is never generated
export interface ParentNullableMap {
    'map'?: { [key: string]: Child; };     // <- Child referenced, never imported
}

tsc then fails with:

models/parent-nullable-map.ts(21,30): error TS2304: Cannot find name 'Child'.

Two things narrow it down:

  1. The nullable type array is what triggers it. With "type": "object" instead of "type": ["null", "object"], the same property imports Child correctly.
  2. additionalProperties is specific. A nullable array of the same $ref ("type": ["null", "array"] with items: {$ref}) is generated correctly, in the same run — included below as a control.

The nullable is also silently dropped on the affected property: 'map'?: { [key: string]: Child; } has no | null, while the control correctly produces 'list'?: Array<Child> | null.

Reproduces with withSeparateModelsAndApi=true.

openapi-generator version

7.24.0 (via the openapitools/openapi-generator-cli:v7.24.0 Docker image). Not tested against earlier versions.

OpenAPI declaration file content or url
{
  "openapi": "3.1.0",
  "info": {"title": "repro", "version": "1.0.0"},
  "paths": {},
  "components": {
    "schemas": {
      "Child": {"type": "object", "properties": {"name": {"type": "string"}}},
      "ParentNullableMap": {
        "type": "object",
        "properties": {
          "map": {"type": ["null", "object"], "additionalProperties": {"$ref": "#/components/schemas/Child"}}
        }
      },
      "ParentNullableArray": {
        "type": "object",
        "properties": {
          "list": {"type": ["null", "array"], "items": {"$ref": "#/components/schemas/Child"}}
        }
      }
    }
  }
}

validate reports no errors on this spec (only "unused model" recommendations, since paths is empty).

Command line used for generation
docker run --rm -v "$PWD:/local" openapitools/openapi-generator-cli:v7.24.0 \
  generate -i /local/minimal.json -g typescript-axios -o /local/out \
  --additional-properties=withSeparateModelsAndApi=true,modelPackage=models,apiPackage=api
Steps to reproduce
  1. Save the spec above as minimal.json.
  2. Run the command above.
  3. models/parent-nullable-map.ts imports Null from ./null and references Child with no import for it. models/null.ts does not exist.
  4. models/parent-nullable-array.ts — the control — imports Child correctly.
  5. Type-check the output, e.g. tsc --noEmit --strict --skipLibCheck --target es2020 --module esnext --moduleResolution bundler models/*.ts, and it fails with TS2304: Cannot find name 'Child'.
Actual output
// models/parent-nullable-map.ts  (BROKEN)
// @ts-ignore
import type { Null } from './null';
export interface ParentNullableMap {
    'map'?: { [key: string]: Child; };
}

// models/parent-nullable-array.ts  (control, correct)
// @ts-ignore
import type { Child } from './child';
export interface ParentNullableArray {
    'list'?: Array<Child> | null;
}
Expected output
// models/parent-nullable-map.ts
import type { Child } from './child';
export interface ParentNullableMap {
    'map'?: { [key: string]: Child; } | null;
}
Related issues/PRs
  • #5601 — same missing-import symptom in typescript-axios with withSeparateModelsAndApi, but a different trigger: additionalProperties of type array with a $ref, on OpenAPI 3.0. That report notes the plain additionalProperties: {$ref} case works, which matches what I see — it only breaks here once the 3.1 nullable type array is added.
  • #9385 — missing import for a nested dictionary-of-collection type; possibly the same import-resolution path.
Suggest a fix

The nullable type array appears to be consumed before the $ref inside additionalProperties is registered as an import, and a Null model is substituted in its place. Two observations that may help locate it:

  • The Null import is emitted for the nullable-type-array case generally, not only when a bare {"type": "null"} schema exists — the spec above contains no such schema, yet the import appears.
  • The // @ts-ignore above generated imports masks the dangling ./null import, so the failure surfaces at the usage site rather than the import, which makes it harder to trace. In the array (control) path the same @ts-ignore line is present but the import is correct.

A workaround for anyone hitting this: pre-process the spec to drop "null" from the type array on properties whose additionalProperties is a $ref (accepting the loss of nullability on those properties), which restores a compiling client.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with minimal.json and run the listed openapi-generator Docker command for typescript-axios. Compare models/parent-nullable-map.ts with models/parent-nullable-array.ts and type-check with the supplied tsc command; done means Child is imported, nullability is preserved, no nonexistent Null model is referenced, and the generated models compile.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, openapi, typescript
Domain
api, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
64/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.