ThomasAribart / json-schema-to-ts

Infer TS types from JSON schemas 📝
MIT License
1.46k stars 31 forks source link

[Feature] add support for `dependencies` keyword #189

Open fargito opened 7 months ago

fargito commented 7 months ago

Hello!

I recently experimented with RJSF, a cool library that generates forms out of JSON Schemas. I needed to validate some objects conditionally, such as in this example.

So I used the dependencies keyword, as advised.

Issue

However, unfortunately, as far as I understand, this keyword is not supported in json-schema-to-ts, which means I have to redefine my types manually...

For example, taking the example from RJSF docs:

import type { RJSFSchema } from "@rjsf/utils";
import type { FromSchema } from "json-schema-to-ts";

const schema = {
  title: "Person",
  type: "object",
  properties: {
    "Do you have any pets?": {
      type: "string",
      enum: ["No", "Yes: One", "Yes: More than one"],
      default: "No",
    },
  },
  required: ["Do you have any pets?"],
  dependencies: {
    "Do you have any pets?": {
      oneOf: [
        {
          properties: {
            "Do you have any pets?": {
              enum: ["No"],
            },
          },
        },
        {
          properties: {
            "Do you have any pets?": {
              enum: ["Yes: One"],
            },
            "How old is your pet?": {
              type: "number",
            },
          },
          required: ["How old is your pet?"],
        },
        {
          properties: {
            "Do you have any pets?": {
              enum: ["Yes: More than one"],
            },
            "Do you want to get rid of any?": {
              type: "boolean",
            },
          },
          required: ["Do you want to get rid of any?"],
        },
      ],
    },
  },
} as const satisfies RJSFSchema;

type MyType = FromSchema<typeof schema>;

This type inferred by json-schema-to-ts is currently:

type MyType = {
    [x: string]: unknown;
    "Do you have any pets?": "No" | "Yes: One" | "Yes: More than one";
}

(everything in dependencies is ignored).

Basic solution

As a basic solution I would expect the resulting type to be:

type MyType = {
    [x: string]: unknown;
    "Do you have any pets?": "No" | "Yes: One" | "Yes: More than one";
    "How old is your pet?"?: number; // can be undefined
    "Do you want to get rid of any?"?: boolean; // can be undefined
}

More advanced solution

In an ideal world, we could have a type closely matching the validation logic, like:

type MyType =
  | {
      [x: string]: unknown;
      "Do you have any pets?": "No";
    }
  | {
      [x: string]: unknown;
      "Do you have any pets?": "Yes: One";
      "How old is your pet?": number;
    }
  | {
      [x: string]: unknown;
      "Do you have any pets?": "Yes: More than one";
      "Do you want to get rid of any?": boolean;
    };

However, I guess the implementation for this advanced solution would be pretty far from trivial.

Question

I was wondering:

WDYT?

Notes

Apparently this keyword will be deprecated in the next draft, but the behaviour that I'm describing will be transferred to the dependentSchemas keyword, so the migration would not be too complicated.

chopfitzroy commented 4 days ago

Just ran into this exact issue. Interested if you have found a nice way to author your own types on top of this?