Repository: vega/ts-json-schema-generator

Issue #1029: Incorrect output for intersection of records with the same property
Created: 2021-11-24T01:51:25Z
This
type Schema = {
    a: {
        b: any
    }
} & {
    a: {
        c: any
    }
};
yields this:
{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$ref": "#/definitions/Schema",
    "definitions": {
        "Schema": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "a": {
                    "type": "object",
                    "properties": {
                        "b": {}
                    },
                    "required": [
                        "b"
                    ],
                    "additionalProperties": false
                }
            },
            "required": [
                "a"
            ]
        }
    }
}
The c property is missing.

Duplicate issue #952: Incorrect schema generation when using recursive mapped types
Created: 2021-09-19T13:31:36Z
Hi there! I have run into a few bugs while trying to generate schemas using recursive mapped types.

Unions are incorrectly merged into a single definition
Comments are lost/replaced with comment from the mapped type
Property modifiers are sometimes lost (like readonly, optional) however I can't yet properly reproduce

Given the following set of types:
type A = {
  /**
   * Some comment on property a
   */
  a: string
}

type B = {
  /**
   * Some comment on property b
   */
  b?: string
  y: string
}

type ComposedWithIntersection = (A | B) & {
  model: string
}

type ComposedSingle = A | B

export type Parent = {
  prop: string
  composed_single: ComposedSingle[]
  composed_with_intersection: ComposedWithIntersection
}

type Primitive = string | number | boolean | null | undefined
type ComplexProperty = Date

/**
 * Comment on mapped type. Should not appear on usages
 */
type SerializedDeep<T> = T extends Primitive
  ? T
  : T extends ComplexProperty
  ? string
  : {
      [P in keyof T]: SerializedDeep<T[P]>
    }

export type SerializedParent = SerializedDeep<Parent>
This will produce the following incorrect schema:

Generated Schema
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "Parent": {
      "additionalProperties": false,
      "properties": {
        "composed_single": {
          "items": {
            "anyOf": [
              {
                "additionalProperties": false,
                "properties": {
                  "a": {
                    "description": "Some comment on property a",
                    "type": "string"
                  }
                },
                "required": ["a"],
                "type": "object"
              },
              {
                "additionalProperties": false,
                "properties": {
                  "b": {
                    "description": "Some comment on property b",
                    "type": "string"
                  },
                  "y": {
                    "type": "string"
                  }
                },
                "required": ["y"],
                "type": "object"
              }
            ]
          },
          "type": "array"
        },
        "composed_with_intersection": {
          "anyOf": [
            {
              "additionalProperties": false,
              "properties": {
                "a": {
                  "description": "Some comment on property a",
                  "type": "string"
                },
                "model": {
                  "type": "string"
                }
              },
              "required": ["a", "model"],
              "type": "object"
            },
            {
              "additionalProperties": false,
              "properties": {
                "b": {
                  "description": "Some comment on property b",
                  "type": "string"
                },
                "model": {
                  "type": "string"
                },
                "y": {
                  "type": "string"
                }
              },
              "required": ["model", "y"],
              "type": "object"
            }
          ]
        },
        "prop": {
          "type": "string"
        }
      },
      "required": ["prop", "composed_single", "composed_with_intersection"],
      "type": "object"
    },
    "SerializedParent": {
      "additionalProperties": false,
      "description": "Comment on mapped type. Should not appear on usages",
      "properties": {
        "composed_single": {
          "description": "Comment on mapped type. Should not appear on usages",
          "items": {
            "additionalProperties": false,
            "description": "Comment on mapped type. Should not appear on usages",
            "properties": {
              "a": {
                "description": "Comment on mapped type. Should not appear on usages",
                "type": "string"
              },
              "b": {
                "description": "Comment on mapped type. Should not appear on usages",
                "type": "string"
              },
              "y": {
                "description": "Comment on mapped type. Should not appear on usages",
                "type": "string"
              }
            },
            "required": ["a", "y"],
            "type": "object"
          },
          "type": "array"
        },
        "composed_with_intersection": {
          "additionalProperties": false,
          "description": "Comment on mapped type. Should not appear on usages",
          "properties": {
            "a": {
              "description": "Comment on mapped type. Should not appear on usages",
              "type": "string"
            },
            "b": {
              "description": "Comment on mapped type. Should not appear on usages",
              "type": "string"
            },
            "model": {
              "description": "Comment on mapped type. Should not appear on usages",
              "type": "string"
            },
            "y": {
              "description": "Comment on mapped type. Should not appear on usages",
              "type": "string"
            }
          },
          "required": ["a", "model", "y"],
          "type": "object"
        },
        "prop": {
          "description": "Comment on mapped type. Should not appear on usages",
          "type": "string"
        }
      },
      "required": ["prop", "composed_single", "composed_with_intersection"],
      "type": "object"
    }
  }
}

Here you can compare the generation of Parent and SerializedParent. You will see that SerializedParent's composed_single and composed_with_intersection properties have merged their unions in an incorrect manner.
Additionally, the comments for property a and property b have been lost and all properties have inherited the comment from the mapped type.
I also want to report having run into an issue with optional properties having their optionality lost (they would become required in the schema) however I haven't been able to successfully reproduce - so I will update this issue once I can. I reworked the mapped type to fix this issue and cannot remember what it was originally.

I would love to help contribute to fixing this issue, but reading through the code it is way over my head :)
Similar issue: #126

Closing comment (2024-05-27T20:24:09Z): Duplicate of #1029