ThomasAribart / json-schema-to-ts

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

Typescript Error when using allOf #35

Closed vikasg603 closed 2 years ago

vikasg603 commented 3 years ago

This is my schema:

{
    title: 'Website Meta Tags Body',
    type: 'object',
    properties: {
        WebsiteID: {
            type: 'integer',
            min: 0
        },
        Body: {
            description: 'Our required Body is Array of object.',
            type: 'array',
            items: {
                type: 'object',
                description: 'In this object we have Type key and based on Type key we have 2 more keys MetaTagID and Body, So we added conditions for that.',
                properties: {
                    Type: {
                        type: 'string',
                        enum: [
                            'INSERT',
                            'UPDATE',
                            'DELETE'
                        ]
                    },
                    MetaTagID: {
                        type: 'integer',
                        min: 1
                    },
                    Body: {
                        type: 'object',
                        properties: {
                            Name: {
                                type: 'string',
                                minLength: 1
                            },
                            Content: {
                                type: 'string',
                                minLength: 1
                            }
                        },
                        required: [
                            'Name',
                            'Content'
                        ]
                    }
                },
                required: [
                    'Type'
                ],
                allOf: [
                    {
                        if: {
                            properties: {
                                Type: {
                                    const: 'INSERT'
                                }
                            },
                            required: [
                                'Type'
                            ]
                        },
                        then: {
                            required: [
                                'Body'
                            ]
                        }
                    },
                    {
                        if: {
                            properties: {
                                Type: {
                                    const: 'UPDATE'
                                }
                            },
                            required: [
                                'Type'
                            ]
                        },
                        then: {
                            required: [
                                'Body',
                                'MetaTagID'
                            ]
                        }
                    },
                    {
                        if: {
                            properties: {
                                Type: {
                                    const: 'DELETE'
                                }
                            },
                            required: [
                                'Type'
                            ]
                        },
                        then: {
                            required: [
                                'MetaTagID'
                            ]
                        }
                    }
                ]
            },
            minItems: 1
        }
    },
    required: [
        'WebsiteID',
        'Body'
    ],
    additionalProperties: false
}

When used FromSchema, Typescript gives some long error, unable to understand the exact error, but after removing allOf it's working properly.

Does anyone of you have any workaround for this issue?

ThomasAribart commented 3 years ago

Hi @vikasg603 ! Good catch, I validate that the schema extends the JSONSchemaV6 definition given by DefinitelyTyped.

However, the if/then/else keywords seem to make it only in the JSONSchemaV7 one: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/json-schema/index.d.ts#L678

It seemed to me that the if/then/else keywords were present in the v6, so I'll dig more into it. If I'm right, then I'll make a PR on DefinitelyTyped. If I'm wrong, then I was planning to rename FromSchema to FromV6Schema and create a parallel FromV7Schema anyway, so now might be a good time to do it.

Meanwhile, if the computed type seems correct to you, you can safely ts-ignore the error for now.

paolochiodi commented 2 years ago

Is there any update on this?

I seem to have a similar error when using something like the attached schema. After some debugging I think the issue is related to having the if/then/else inside the allOf. If the if/then/else instead is at the "root" level the type is accepted.

This won't work:

const Test = {
  type: "object",
  properties: {
    condition: {
      type: "string",
      enum: ["A", "B"]
    },
    "other": {
      type: "string"
    }
  },
  required: ["condition"],
  allOf: [{
    if: {
      properties: {
        condition: { const: "A" }
      }
    },
    then: {
      required: ["other"]
    }
  }],
  additionalProperties: false
} as const

type TestSchema = FromSchema<typeof Test>

This will work:

const Test = {
  type: "object",
  properties: {
    condition: {
      type: "string",
      enum: ["A", "B"]
    },
    "other": {
      type: "string"
    }
  },
  required: ["condition"],
  if: {
    properties: {
      condition: { const: "A" }
    }
  },
  then: {
    required: ["other"]
  },
  additionalProperties: false
} as const

type TestSchema = FromSchema<typeof Test>

In this reduced example they two schemas are equivalent, but it's not always possible to do so (i.e. when you need multiple if/then/else you need to wrap them in the allOf)

ThomasAribart commented 2 years ago

Hi @paolochiodi !

Can you try it with v2.0.1 ?

It seems to work fine now:

const Test = {
  type: "object",
  properties: {
    condition: {
      type: "string",
      enum: ["A", "B"],
    },
    other: {
      type: "string",
    },
  },
  required: ["condition"],
  allOf: [
    {
      if: {
        properties: {
          condition: { const: "A" },
        },
      },
      then: {
        required: ["other"],
      },
    },
  ],
  additionalProperties: false,
} as const;

type TestSchema = FromSchema<typeof Test, { parseIfThenElseKeywords: true }>;

type ResultType = {
    condition: "A";
    other: string;
} | {
    other?: string | undefined;
    condition: "A" | "B";
}