openapi-ts / openapi-typescript

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

Self referential types fail to compile #1603

Open chitalian opened 3 months ago

chitalian commented 3 months ago

Description

Having a self referential type like...

        "Json": {
                "anyOf": [
                    {
                        "type": "string"
                    },
                    {
                        "type": "number",
                        "format": "double"
                    },
                    {
                        "type": "boolean"
                    },
                    {
                        "properties": {},
                        "additionalProperties": {
                            "$ref": "#/components/schemas/Json"
                        },
                        "type": "object"
                    },
                    {
                        "items": {
                            "$ref": "#/components/schemas/Json"
                        },
                        "type": "array"
                    }
                ],
                "nullable": true
            },

Causes typescript to be generate like this

export interface components {
  schemas: {
    Json: (string | number | boolean | {
      [key: string]: components["schemas"]["Json"];
    } | components["schemas"]["Json"][]) | null;

Causing this failure...


./lib/clients/jawnTypes.ts:34:5
Type error: 'Json' is referenced directly or indirectly in its own type annotation.

  32 |       phoneNumbers: string[];
  33 |     };
> 34 |     Json: (string | number | boolean | {
     |     ^
  35 |       [key: string]: components["schemas"]["Json"];
  36 |     } | components["schemas"]["Json"][]) | null;
  37 |     /** @enum {string} */
Name Version
openapi-typescript 6.7.5
Node.js 18.17.1
OS + version macOS 14.3

Reproduction

wget https://raw.githubusercontent.com/Helicone/helicone/53cbb77419d533bbe20de8262a3887786e3c0faa/valhalla/jawn/src/tsoa-build/swagger.json

openapi-typescript  swagger.json -o types.ts

Expected result

We can somehow build the typescript to move any self-referential types to it's own type like this?

type JsonValue = string | number | boolean | null | JsonArray | JsonObject;
interface JsonArray extends Array<JsonValue> {}
interface JsonObject {
  [key: string]: JsonValue;
}

export interface components {
  schemas: {
    User: {
      /** Format: double */
      id: number;
      email: string;
      name: string;
      /** @enum {string} */
      status?: "Happy" | "Sad";
      /** @enum {string} */
      status2?: "Happy" | "Sad";
      phoneNumbers: string[];
    };
    Json: JsonObject;

Checklist

hbroer commented 2 months ago

Similar problem here. The (reduced) code looks like this:

    PackageTreePermittedResponseV2: WithRequired<{
      permission: "FULL";
    } &  Omit<components["schemas"]["PackageTreeResponseV2"], "permission"> & ({
      packages?: components["schemas"]["PackageTreeResponseV2"][];
    }), "id" | "permission" | "packages">;
    PackageTreeResponseV2: {
      id: string;
      permission: string;
    } & (components["schemas"]["PackageTreePermittedResponseV2"]);

Errors: TS2502:  PackageTreePermittedResponseV2  is referenced directly or indirectly in its own type annotation. TS2502:  PackageTreeResponseV2  is referenced directly or indirectly in its own type annotation.

   "PackageTreePermittedResponseV2": {
        "required": [
          ..........................................................
          "id",
          "packages",
          "permission",
          ..........................................................
        ],
        "type": "object",
        "allOf": [
          {
            "$ref": "#/components/schemas/PackageTreeResponseV2"
          },
          {
            "type": "object",
            "properties": {
              ..........................................................
              "packages": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/PackageTreeResponseV2"
                }
              },
              ..........................................................
            }
          }
        ]
      },
      "PackageTreeResponseV2": {
        "title": "PackageTreeResponseV2",
        "required": ["id", "permission"],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "permission": {
            "type": "string"
          }
        },
        "discriminator": {
          "propertyName": "permission",
          "mapping": {
            "FULL": "#/components/schemas/PackageTreePermittedResponseV2",
            ..........................................................
          }
        },
        "oneOf": [
          {
            "$ref": "#/components/schemas/PackageTreePermittedResponseV2"
          },
          {
            "$ref": "#/components/schemas/PackageTreeUnauthorisedResponseV2"
          }
        ]
      },