RicoSuter / NSwag

The Swagger/OpenAPI toolchain for .NET, ASP.NET Core and TypeScript.
http://NSwag.org
MIT License
6.71k stars 1.24k forks source link

nswag openapi2tsclient: issue with toJSON when object containing array of nullable objects #4975

Open YMER13 opened 1 month ago

YMER13 commented 1 month ago

Hello,

The following OpenAPI definition contains a GetItResponse schema having 1 property "values" which is an array of a nullable object.

{
  "x-generator": "NSwag v14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))",
  "openapi": "3.0.0",
  "info": {
    "title": "TimeDataV1",
    "version": "1.0.0"
  },
  "paths": {
    "/api/getIt": {
      "get": {
        "tags": [
          "TimeData"
        ],
        "operationId": "TimeData_GetIt",
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetItResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "GetItResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "values"
        ],
        "properties": {
          "values": {
            "type": "array",
            "items": {
              "nullable": true,
              "oneOf": [
                {
                  "$ref": "#/components/schemas/It"
                }
              ]
            }
          }
        }
      },
      "It": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "value"
        ],
        "properties": {
          "value": {
            "type": "boolean"
          }
        }
      }
    }
  }
}

When generating the typescript client through nswag, the output contains the following:

export class GetItResponse implements IGetItResponse {
    values!: (It | undefined)[];

    constructor(data?: IGetItResponse) {
        if (data) {
            for (var property in data) {
                if (data.hasOwnProperty(property))
                    (<any>this)[property] = (<any>data)[property];
            }
            if (data.values) {
                this.values = [];
                for (let i = 0; i < data.values.length; i++) {
                    let item = data.values[i];
                    this.values[i] = item && !(<any>item).toJSON ? new It(item) : <It>item;
                }
            }
        }
        if (!data) {
            this.values = [];
        }
    }

    init(_data?: any) {
        if (_data) {
            if (Array.isArray(_data["values"])) {
                this.values = [] as any;
                for (let item of _data["values"])
                    this.values!.push(It.fromJS(item));
            }
        }
    }

    static fromJS(data: any): GetItResponse {
        data = typeof data === 'object' ? data : {};
        let result = new GetItResponse();
        result.init(data);
        return result;
    }

    toJSON(data?: any) {
        data = typeof data === 'object' ? data : {};
        if (Array.isArray(this.values)) {
            data["values"] = [];
            for (let item of this.values)
                data["values"].push(item.toJSON());
        }
        return data;
    }
}

The toJSON function iterates over the values array and call .ToJSON() on each value. However, since the value can be undefined, I get the following error when compiling:

error TS2532: Object is possibly 'undefined'. => data["values"].push(item.toJSON());

I guess the generated toJSON should contain data["values"].push(item?.toJSON()); instead of data["values"].push(item.toJSON());

davesmits commented 3 days ago

same issue; did you find a work around?

YMER13 commented 2 days ago

@davesmits, for now I changed my API not to expose array of nullable objects. If you find any workaround, it is welcome.