Skip to content

[BUG][typescript-fetch] self-referencing discriminator mapping causes infinite recursion #21955

@sermler

Description

@sermler

Bug Report Checklist

  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description
openapi-generator version

7.15.0 and older

OpenAPI declaration file content or url

If you post the code inline, please wrap it with

openapi: 3.0.3
info:
  title: Minimal
  description: Api to reproduce bug
  version: 0.5.1-SNAPSHOT.0
tags:
  - name: test
servers:
  - url: http://localhost:8080
paths:
  /api/v1/test:
    get:
      tags:
        - test
      operationId: test
      responses:
        "200":
          description: test operation
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/TestBaseDto"

components:
  schemas:
    TestObjectType:
      type: string
      enum:
        - TEST1
        - TEST2
    TestBaseDto:
      type: object
      properties:
        something:
          type: string
        testObjectType:
          $ref: "#/components/schemas/TestObjectType"
      discriminator:
        propertyName: testObjectType
        mapping:
          TEST2: '#/components/schemas/ExtendDto'
          TEST1: '#/components/schemas/TestBaseDto'
    ExtendDto:
      allOf:
        - $ref: '#/components/schemas/TestBaseDto'
        - type: object
          properties:
            someItems:
              type: array
              items:
                $ref: '#/components/schemas/TestBaseDto'
Generation Details

If you have a discirminator and one of the mapped models references the base model, it will generate a slef import and generates a *FromJSONTyped funtion which causes infinite recursion:

/* tslint:disable */
/* eslint-disable */
/**
 * Minimal
 * Api to reproduce bug
 *
 * The version of the OpenAPI document: 0.5.1-SNAPSHOT.0
 * 
 *
 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 * https://openapi-generator.tech
 * Do not edit the class manually.
 */

import { mapValues } from '../runtime';
import type { TestObjectType } from './TestObjectType';
import {
    TestObjectTypeFromJSON,
    TestObjectTypeFromJSONTyped,
    TestObjectTypeToJSON,
    TestObjectTypeToJSONTyped,
} from './TestObjectType';

import { type TestBaseDto, TestBaseDtoFromJSONTyped, TestBaseDtoToJSON, TestBaseDtoToJSONTyped } from './TestBaseDto';
import { type ExtendDto, ExtendDtoFromJSONTyped, ExtendDtoToJSON, ExtendDtoToJSONTyped } from './ExtendDto';
/**
 * 
 * @export
 * @interface TestBaseDto
 */
export interface TestBaseDto {
    /**
     * 
     * @type {string}
     * @memberof TestBaseDto
     */
    something?: string;
    /**
     * 
     * @type {TestObjectType}
     * @memberof TestBaseDto
     */
    testObjectType?: TestObjectType;
}



/**
 * Check if a given object implements the TestBaseDto interface.
 */
export function instanceOfTestBaseDto(value: object): value is TestBaseDto {
    return true;
}

export function TestBaseDtoFromJSON(json: any): TestBaseDto {
    return TestBaseDtoFromJSONTyped(json, false);
}

export function TestBaseDtoFromJSONTyped(json: any, ignoreDiscriminator: boolean): TestBaseDto {
    if (json == null) {
        return json;
    }
    if (!ignoreDiscriminator) {
        if (json['testObjectType'] === 'TEST1') {
            return TestBaseDtoFromJSONTyped(json, ignoreDiscriminator);
        }
        if (json['testObjectType'] === 'TEST2') {
            return ExtendDtoDtoFromJSONTyped(json, ignoreDiscriminator);
        }
    }
    return {
        
        'something': json['something'] == null ? undefined : json['something'],
        'testObjectType': json['testObjectType'] == null ? undefined : TestObjectTypeFromJSON(json['testObjectType']),
    };
}

export function TestBaseDtoToJSON(json: any): TestBaseDto {
    return TestBaseDtoToJSONTyped(json, false);
}

export function TestBaseDtoToJSONTyped(value?: TestBaseDto | null, ignoreDiscriminator: boolean = false): any {
    if (value == null) {
        return value;
    }

    if (!ignoreDiscriminator) {
        switch (value['testObjectType']) {
            case 'TEST1':
                return TestBaseDtoToJSONTyped(value as TestBaseDto, ignoreDiscriminator);
            case 'TEST2':
                return ExtendDtoToJSONTyped(value as ExtendDto, ignoreDiscriminator);
            default:
                return value;
        }
    }

    return {
        
        'something': value['something'],
        'testObjectType': TestObjectTypeToJSON(value['testObjectType']),
    };
}

Expected (Fix):

/* tslint:disable */
/* eslint-disable */
/**
 * Minimal
 * Api to reproduce bug
 *
 * The version of the OpenAPI document: 0.5.1-SNAPSHOT.0
 * 
 *
 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 * https://openapi-generator.tech
 * Do not edit the class manually.
 */

import { mapValues } from '../runtime';
import type { TestObjectType } from './TestObjectType';
import {
    TestObjectTypeFromJSON,
    TestObjectTypeFromJSONTyped,
    TestObjectTypeToJSON,
    TestObjectTypeToJSONTyped,
} from './TestObjectType';

import { type ExtendDto, ExtendDtoFromJSONTyped, ExtendDtoToJSON, ExtendDtoToJSONTyped } from './ExtendDto';
/**
 * 
 * @export
 * @interface TestBaseDto
 */
export interface TestBaseDto {
    /**
     * 
     * @type {string}
     * @memberof TestBaseDto
     */
    something?: string;
    /**
     * 
     * @type {TestObjectType}
     * @memberof TestBaseDto
     */
    testObjectType?: TestObjectType;
}



/**
 * Check if a given object implements the TestBaseDto interface.
 */
export function instanceOfTestBaseDto(value: object): value is TestBaseDto {
    return true;
}

export function TestBaseDtoFromJSON(json: any): TestBaseDto {
    return TestBaseDtoFromJSONTyped(json, false);
}

export function TestBaseDtoFromJSONTyped(json: any, ignoreDiscriminator: boolean): TestBaseDto {
    if (json == null) {
        return json;
    }
    if (!ignoreDiscriminator) {
        if (json['testObjectType'] === 'TEST1') {
            return TestBaseDtoFromJSONTyped(json, true);
        }
        if (json['testObjectType'] === 'TEST2') {
            return ExtendDtoDtoFromJSONTyped(json, ignoreDiscriminator);
        }
    }
    return {
        
        'something': json['something'] == null ? undefined : json['something'],
        'testObjectType': json['testObjectType'] == null ? undefined : TestObjectTypeFromJSON(json['testObjectType']),
    };
}

export function TestBaseDtoToJSON(json: any): TestBaseDto {
    return TestBaseDtoToJSONTyped(json, false);
}

export function TestBaseDtoToJSONTyped(value?: TestBaseDto | null, ignoreDiscriminator: boolean = false): any {
    if (value == null) {
        return value;
    }

    if (!ignoreDiscriminator) {
        switch (value['testObjectType']) {
            case 'TEST1':
                return TestBaseDtoToJSONTyped(value as TestBaseDto, ignoreDiscriminator);
            case 'TEST2':
                return ExtendDtoToJSONTyped(value as ExtendDto, ignoreDiscriminator);
            default:
                return value;
        }
    }

    return {
        
        'something': value['something'],
        'testObjectType': TestObjectTypeToJSON(value['testObjectType']),
    };
}
Steps to reproduce

{
"$schema": "https://raw.githubusercontent.com/OpenAPITools/openapi-generator-cli/refs/heads/master/apps/generator-cli/src/config.schema.json",
"spaces": 2,
"generator-cli": {
"version": "7.15.0",
"generators": {
"typescript-client-sdk": {
"generatorName": "typescript-fetch",
"inputSpec": "api-specification.yml",
"output": "#{cwd}/sdks/typescript-client-sdk",
"additionalProperties": {
"enumPropertyNaming": "original",
"enumUnknownDefaultCase": true
}
}
}

Related issues/PRs
Suggest a fix

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions