OpenAPITools / OpenAPITools/openapi-generator

[BUG][Typescript] Incorrect response type handling for oneOf strategy

Open
#9,305 4 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Issue: Bug
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)?
  • 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
  • Is there a way to not have a wrapper object as an input parameter and instead just take an union type?
  • The return types are correct but it seems not to be handled correctly, a bug perhaps?
  • If everything works as intended how can I change my schema so the generated client has a method that can parse the response and type it correctly?
openapi-generator version

5.1.0

OpenAPI declaration file content or url

specs.yaml

openapi: 3.0.1
info:
  title: Sample API
  description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.
  version: 0.1.9
servers:
  - url: http://api.example.com/v1
    description: Optional server description, e.g. Main (production) server
  - url: http://staging-api.example.com
    description: Optional server description, e.g. Internal staging server for testing
paths:
  /pets:
    post:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/Cat'
                - $ref: '#/components/schemas/Dog'
              discriminator:
                propertyName: pet_type
                mapping:
                  obj1: '#/components/schemas/Cat'
                  obj2: '#/components/schemas/Dog'
      responses:
        '200':
          description: Created
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/Cat'
                  - $ref: '#/components/schemas/Dog'
                discriminator:
                  propertyName: pet_type
                  mapping:
                    obj1: '#/components/schemas/Cat'
                    obj2: '#/components/schemas/Dog'
components:
  schemas:
    Pet:
      type: object
      required:
        - pet_type
      properties:
        pet_type:
          type: string
      discriminator:
        propertyName: pet_type
    Dog:
      type: object
      allOf:
        - $ref: '#/components/schemas/Pet'
        - type: object
      properties:
        bark:
          type: boolean
        breed:
          type: string
          enum: [Dingo, Husky, Retriever, Shepherd]
    Cat:
      type: object
      allOf:
        - $ref: '#/components/schemas/Pet'
        - type: object
      properties:
        hunts:
          type: boolean
        age:
          type: integer
Generation Details
java -jar openapi-generator-cli.jar generate -i specs.yaml -g typescript-fetch -o tsclient/
Output
...
export interface PetsPostRequest {
    catDog?: Cat | Dog;
}

/**
 * 
 */
export class DefaultApi extends runtime.BaseAPI {

    /**
     */
    async petsPostRaw(requestParameters: PetsPostRequest): Promise<runtime.ApiResponse<Cat | Dog>> {
        const queryParameters: any = {};

        const headerParameters: runtime.HTTPHeaders = {};

        headerParameters['Content-Type'] = 'application/json';

        const response = await this.request({
            path: `/pets`,
            method: 'POST',
            headers: headerParameters,
            query: queryParameters,
            body: Cat | DogToJSON(requestParameters.catDog),
        });

        return new runtime.JSONApiResponse(response, (jsonValue) => Cat | DogFromJSON(jsonValue));
    }

    /**
     */
    async petsPost(requestParameters: PetsPostRequest): Promise<Cat | Dog> {
        const response = await this.petsPostRaw(requestParameters);
        return await response.value();
    }
Expected, something like
async petsPostRaw(pet: Cat | Dog | undefined ): Promise<runtime.ApiResponse<Cat | Dog>> {
        const queryParameters: any = {};

        const headerParameters: runtime.HTTPHeaders = {};

        headerParameters['Content-Type'] = 'application/json';
        const response = await this.request({
            path: `/pets`,
            method: 'POST',
            headers: headerParameters,
            query: queryParameters,
            body: pet.petType === 'Cat' ? CatToJSON() : pet.petType === 'Dog' ? DogToJSON() : undefined,
        });

        return new runtime.JSONApiResponse(response, 
            (jsonValue) => {
            const typedValue = 
                pet.petType === 'Cat' ? CatFromJSON(jsonValue) 
                : pet.petType === 'Dog' ? DogFromJSON(jsonValue) 
                : undefined;

            if(!typedValue){
                throw new Error(`No type matching "${pet.petType}" found`);
            }
            
            return typedValue;
        });
    }

    /**
     */
    async petsPost(pet: Cat | Dog): Promise<Cat | Dog> {
        const response = await this.petsPostRaw(pet);
        return await response.value();
    }
Related issues/PRs
  • Probably more than one regarding oneOf, anyOf, allOf

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 by reproducing the issue with the supplied specs.yaml and the typescript-fetch generation command. Compare the generated petsPost request and response handling with the expected Cat | Dog behavior, then trace the oneOf serialization and deserialization entry points. Done means the generated client handles the union without the incorrect wrapper and preserves the expected response typing.

Written by the indexing model from the issue text.

Assessment

Tech stack
openapi, typescript
Domain
api, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.