Skip to content

Discriminator mapping values ignored in multi-file schemas — uses $ref name instead of mapping key #3399

@littlemyx

Description

@littlemyx

Description

When using a multi-file OpenAPI 3.0.3 schema with discriminator.mapping defined on a oneOf, the generated TypeScript types use the resolved $ref name (with file prefix) as the discriminator const value instead of the mapping key.

For example, given a discriminator.mapping of alpha: "#/AlphaProviderConfigResponse" defined in providers.yaml, the generated type uses type: 'providers_AlphaProviderConfigResponse' instead of type: 'alpha'.

This causes the discriminator property to become never because the intersection of 'providers_AlphaProviderConfigResponse' & 'alpha' (from the inner type's own type enum) is impossible, making the entire union type never.

Expected:

export type ProvidersProviderConfigResponse =
  | ({ type: 'alpha' } & ProvidersAlphaProviderConfigResponse)
  | ({ type: 'beta' } & ProvidersBetaProviderConfigResponse);

Actual:

export type ProvidersProviderConfigResponse =
  | ({ type: 'providers_AlphaProviderConfigResponse' } & ProvidersAlphaProviderConfigResponse)
  | ({ type: 'providers_BetaProviderConfigResponse' } & ProvidersBetaProviderConfigResponse);
// Each branch is `never` because e.g. 'providers_AlphaProviderConfigResponse' & 'alpha' = never

The Transformers plugin also emits a warning confirming the wrong const values:

Transformers warning: schema {"items":[{"items":[{"properties":{"type":{"const":"providers_AlphaProviderConfigResponse","type":"string"}} ...

Reproducible example or configuration

File structure:

schema/
├── openapi.yaml        # main spec
└── providers.yaml      # provider schemas

OpenAPI specification (optional)

openapi.yaml:

openapi: 3.0.3
info:
  title: Minimal Reproduction
  version: 1.0.0
paths:
  /providers:
    get:
      summary: List providers
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "providers.yaml#/ProviderConfigResponse"
components:
  schemas:
    ProviderConfigResponse:
      $ref: "providers.yaml#/ProviderConfigResponse"

providers.yaml:

ProviderConfigResponse:
  oneOf:
    - $ref: "#/AlphaProviderConfigResponse"
    - $ref: "#/BetaProviderConfigResponse"
  discriminator:
    propertyName: type
    mapping:
      alpha: "#/AlphaProviderConfigResponse"
      beta: "#/BetaProviderConfigResponse"

AlphaProviderConfigResponse:
  type: object
  required: [type, url]
  properties:
    type:
      type: string
      enum: [alpha]
    url:
      type: string

BetaProviderConfigResponse:
  type: object
  required: [type, region]
  properties:
    type:
      type: string
      enum: [beta]
    region:
      type: string

Expected generated types:

export type ProvidersProviderConfigResponse =
  | ({ type: 'alpha' } & ProvidersAlphaProviderConfigResponse)
  | ({ type: 'beta' } & ProvidersBetaProviderConfigResponse);

Actual generated types:

export type ProvidersProviderConfigResponse =
  | ({ type: 'providers_AlphaProviderConfigResponse' } & ProvidersAlphaProviderConfigResponse)
  | ({ type: 'providers_BetaProviderConfigResponse' } & ProvidersBetaProviderConfigResponse);
// Both branches become `never` due to conflicting type literals

System information (optional)

  • @hey-api/openapi-ts: 0.84.4
  • Node.js: v24.12.0
  • OpenAPI spec version: 3.0.3
  • Multi-file schema (external $refs between YAML files)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug 🔥Broken or incorrect behavior.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions