ThomasAribart / json-schema-to-ts

Infer TS types from JSON schemas 📝
MIT License
1.43k stars 30 forks source link

Seemly non-recursive schema is hitting a recursive schema bug #112

Open Georift opened 1 year ago

Georift commented 1 year ago

It's my first time using the library, so I might be mis-understanding #96, however I think I've found a schema which doesn't have a recursive ref loop. But I'm seeing an error:

Type of property 'detail' circularly references itself in mapped type '{ account: { type: "primitive"; value: string; isSerialized: false; deserialized: never; }; detail: Never; "detail-type": { type: "primitive"; value: string; isSerialized: false; deserialized: never; }; ... 5 more ...; version: { ...; }; }'.

Here is my example code, .detail references a definition, however it doesn't recurse forever, should this be expected to work?

import { FromSchema } from "json-schema-to-ts";

export const s3CreateEventSchema = {
  $schema: "http://json-schema.org/draft-04/schema#",
  title: "ObjectCreated",
  definitions: {
    Bucket: {
      properties: {
        name: {
          type: "string",
        },
      },
      required: ["name"],
      type: "object",
    },
    Object: {
      properties: {
        etag: {
          type: "string",
        },
        key: {
          type: "string",
        },
        sequencer: {
          type: "string",
        },
        size: {
          type: "number",
        },
        "version-id": {
          type: "string",
        },
      },
      required: ["etag", "key", "sequencer", "size"],
      type: "object",
    },
    ObjectCreated: {
      properties: {
        bucket: {
          $ref: "#/definitions/Bucket",
        },
        object: {
          $ref: "#/definitions/Object",
        },
        reason: {
          type: "string",
        },
        "request-id": {
          type: "string",
        },
        requester: {
          type: "string",
        },
        "source-ip-address": {
          type: "string",
        },
        version: {
          type: "string",
        },
      },
      required: [
        "bucket",
        "object",
        "reason",
        "requester",
        "request-id",
        "source-ip-address",
        "version",
      ],
      type: "object",
    },
  },
  properties: {
    account: {
      type: "string",
    },
    detail: {
      $ref: "#/definitions/ObjectCreated",
    },
    "detail-type": {
      type: "string",
    },
    id: {
      type: "string",
    },
    region: {
      type: "string",
    },
    resources: {
      items: {
        type: "string",
      },
      type: "array",
    },
    source: {
      type: "string",
    },
    time: {
      format: "date-time",
      type: "string",
    },
    version: {
      type: "string",
    },
  },
  required: [
    "account",
    "detail",
    "detail-type",
    "id",
    "region",
    "resources",
    "source",
    "time",
    "version",
  ],
  type: "object",
  "x-amazon-events-detail-type": "Object Created",
  "x-amazon-events-source": "aws.s3",
} as const;

export type S3CreateEvent = FromSchema<typeof s3CreateEventSchema>;
Georift commented 1 year ago

I just tried a simpler case just to check my assumptions, I found that if I do any kind of definition I run into this problem:

const anotherTest = {
  $schema: "http://json-schema.org/draft-04/schema",
  title: "Test Schema",
  type: "object",
  definitions: {
    Larger: {
      type: "string",
    },
  },
  properties: {
    testing: {
      type: "boolean",
    },
    again: {
      $ref: "#/definitions/Larger",
    },
  },
  required: ["testing", "again"],
} as const;

type AnotherTest = FromSchema<typeof anotherTest>;
// => never

Interestingly if I drop "again" from the required arguments I still see an error but TS kind of partially infers the type rather than just never:

type AnotherTest = {
    [x: string]: unknown;
    again?: any;
    testing: any;
}

Does this mean #96 actually means any reference is unsupported?