openapi-ts / openapi-typescript

Generate TypeScript types from OpenAPI 3 specs
https://openapi-ts.dev
MIT License
5.2k stars 419 forks source link

Array of response object in schema.yaml / schema.json does not result in array type in schema.d.ts #1421

Open mklueh opened 8 months ago

mklueh commented 8 months ago

Description Array type of response object does not result in array type in schema

 "SomeResponseObject" : {
        "type" : "object",
        "properties" : {
          "page" : {
            "format" : "int64",
            "type" : "integer"
          },
          "pages" : {
            "format" : "int64",
            "type" : "integer"
          },
          "items" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/ResponseListItem"
            },
             "oneOf" : [ {
              "$ref" : "#/components/schemas/SomeItem"
            }, {
              "$ref" : "#/components/schemas/SomeOtherItem"
            } ]
          }
        }
      },

will result in

    SomeResponseObject: {
      /** Format: int64 */
      page?: number;
      /** Format: int64 */
      pages?: number;
      items?: components["schemas"]["SomeItem"] | components["schemas"]["SomeOtherItem"];
    };

Which does not represent the array but just a single item.

I've run Redocly and there are some issues, but nothing related to the object I'm referring to.

BTW the openapi.json / yaml is autogenerated by my Quarkus Application and this is the originating Java class:

@Data
@Builder
@AllArgsConstructor
public class SomeResponseObject {

    private long page;

    private long pages;

    @Schema(type = ARRAY, oneOf = {SomeItem.class, SomeOtherItem.class})
    private List<ResponseListItemDto> items;
}

I'm using

✨ openapi-typescript 6.7.0

Expected result

(in case it’s not obvious)

Checklist

drwpow commented 8 months ago

I think the oneOf on the array is throwing it off—since oneOf is mutually exclusive, it can’t easily coexist with other properties (or at least, it’s unclear how the original author intends for this to be resolved). Perhaps this is semantically valid but logically you really can only use oneOf on object-type Schema Objects.

Or if that property really is polymorphic, then I’d suggest moving the type: "array", items: … inside the oneOf so they don’t conflict.

mklueh commented 8 months ago

@drwpow thanks.

In my case the property is polymorphic. So this is basically not part of the specification?

Baeldung seems to confidently use it that way and this site is pretty reliable, which is wondering me now. https://www.baeldung.com/openapi-array-of-varying-types#1-oneof-keyword

Or if that property really is polymorphic, then I’d suggest moving the type: "array", items: … inside the oneOf so they don’t conflict.

Could you please show me how exactly that would look like in my schema? Not sure how it would project to the Java implementation.

Would I have to annotate the base class then with @Schema(type = ARRAY, oneOf = {SomeItem.class, SomeOtherItem.class})?

drwpow commented 8 months ago

Ah I got it backwards—it’s your items that are polymorphic and not the top-level property.

I’m not too familiar with that Java library you’re using, but you’ll need to have the oneOf on the items of the array, and not the array itself.

drwpow commented 8 months ago

So if we adjusted your original code, this is how it should look:

  "SomeResponseObject": {
    "type": "object",
      "properties": {
        "page": {
          "format": "int64",
          "type": "integer"
        },
        "pages": {
          "format": "int64",
          "type": "integer"
        },
        "items": {
          "type": "array",
          "items": {
-           "$ref": "#/components/schemas/ResponseListItem"
+           "oneOf": [
+             {"$ref": "#/components/schemas/ResponseListItem"},
+             {"$ref": "#/components/schemas/SomeItem"},
+             {"$ref": "#/components/schemas/SomeOtherItem"}
+           ]
          },
-         "oneOf": [
-           {"$ref": "#/components/schemas/SomeItem"},
-           {"$ref": "#/components/schemas/SomeOtherItem"}
-         ]
        }
      }
    }