OpenAPITools / openapi-generator

OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)
https://openapi-generator.tech
Apache License 2.0
21.38k stars 6.47k forks source link

[Question] [typescript-axios] How to avoid manually passing the discriminator/mapping values #15323

Open johanbv opened 1 year ago

johanbv commented 1 year ago

Hey!

After generating the typescript-axios client using the following scheme which includes a discriminator, propertyName ($type) and mapping

scheme ```json { "openapi": "3.0.1", "info": { "title": "Example API", "version": "1.0.0" }, "components": { "schemas": { "Pet": { "type": "object", "properties": { "name": { "type": "string" }, "$type": { "type": "string" } }, "required": [ "name", "$type" ], "discriminator": { "propertyName": "$type", "mapping": { "dog": "#/components/schemas/Dog", "cat": "#/components/schemas/Cat" } } }, "Dog": { "allOf": [ { "$ref": "#/components/schemas/Pet" }, { "type": "object", "properties": { "breed": { "type": "string" } }, "required": [ "breed" ] } ] }, "Cat": { "allOf": [ { "$ref": "#/components/schemas/Pet" }, { "type": "object", "properties": { "clawed": { "type": "boolean" } }, "required": [ "clawed" ] } ] } } }, "paths": { "/pets": { "get": { "summary": "Get a list of pets", "responses": { "200": { "description": "A list of pets", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Pet" } } } } } } } } } } ```

the generated code includes the discriminator propertyName ($type) as any other property of the model:

/**
 * 
 * @export
 * @interface Pet
 */
export interface Pet {
    /**
     * 
     * @type {string}
     * @memberof Pet
     */
    'name': string;
    /**
     * 
     * @type {string}
     * @memberof Pet
     */
    '$type': string;
}

With this code, we will have to manually pass on $type the corresponding value of the mapping to any instance of Cat or Dog that we create before sending it to the api.

Is there a way to avoid doing that with this client?

Looking at the generated code of the same scheme but using the typescript-fetch client we found that there is a function that solve the problem:

export function PetFromJSONTyped(json: any, ignoreDiscriminator: boolean): Pet {
    if ((json === undefined) || (json === null)) {
        return json;
    }
    if (!ignoreDiscriminator) {
        if (json['$type'] === 'cat') {
            return CatFromJSONTyped(json, true);
        }
        if (json['$type'] === 'dog') {
            return DogFromJSONTyped(json, true);
        }
    }
    return {

        'name': json['name'],
        '$type': json['$type'],
    };
}

Is there a way to generate something similar on typescript-axios?

Here is the repo with the typescript-axios client code used on this question: https://github.com/johanbv/typescript-axios-client

federicocarbonell commented 1 year ago

I'm having the same issue.

ChristopherChudzicki commented 1 year ago

For what it's worth, the example spec below is similar and produces very reasonable typescript. The main differences are:

That's how I'd always done discriminated unions in the past. It's a little more verbose (not too bad) than @johanbv's example. (Both specs are valid; @johanbv's is. the last example discussed in https://swagger.io/specification/#discriminator-object).

Show spec ```yaml openapi: 3.0.1 info: title: Example API version: 1.0.0 components: schemas: PetShared: type: object properties: name: type: string required: - name - "$type" Pet: type: object oneOf: - "$ref": "#/components/schemas/Dog" - "$ref": "#/components/schemas/Cat" discriminator: propertyName: "$type" mapping: dog: "#/components/schemas/Dog" cat: "#/components/schemas/Cat" Dog: allOf: - "$ref": "#/components/schemas/PetShared" - type: object properties: breed: type: string required: - breed Cat: allOf: - "$ref": "#/components/schemas/PetShared" - type: object properties: clawed: type: boolean required: - clawed paths: "/pets": get: summary: Get a list of pets responses: '200': description: A list of pets content: application/json: schema: type: array items: "$ref": "#/components/schemas/Pet" ```

Producing (I removed some comments):

export interface Cat {
    'name': string;
    'clawed': boolean;
}
export interface Dog {
    'name': string;
    'breed': string;
}
export type Pet = { $type: 'cat' } & Cat | { $type: 'dog' } & Dog;