Skip to content

Combination of intersected type and type transform can cause compilation to incorrect object type for non-primitive child properties #2294

@robcmills

Description

@robcmills

The combination of an intersected type and type transform (e.g. any TypeScript Utility Type),
can cause compilation to incorrect object type,
on any non-primitive (e.g. Array, Tuple) child properties.

type Intersected = {
  /** Array of vertex indices. */
  v: number[];
} & {};

export type Transformed = Required<Intersected>

compiles to incorrect json schema:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$ref": "#/definitions/B",
  "definitions": {
    "Transformed": {
      "type": "object",
      "properties": {
        "v": {
          "type": "object",
          "properties": {},
          "description": "Array of vertex indices.",
        },
      },
      "additionalProperties": false,
    },
  },
}

It appears to be only the combination of the intersected type and the type
transform that causes this issue. If you remove either, schema generation is
correct:

export type Intersected = {
  /** Array of vertex indices. */
  v: number[];
} & {};

type NonIntersected = {
  /** Array of vertex indices. */
  v: number[];
};

export type Transformed = Required<NonIntersected>

generates correct json schema:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "Intersected": {
      "type": "object",
      "properties": {
        "v": {
          "type": "array",
          "items": {
            "type": "number",
          },
          "description": "Array of vertex indices.",
        },
      },
      "required": [ "v" ],
    },
    "Transformed": {
      "type": "object",
      "properties": {
        "v": {
          "type": "array",
          "items": {
            "type": "number",
          },
          "description": "Array of vertex indices.",
        },
      },
    },
  },
}

Also, if the child property type is a primitive, the issue does not occur:

type Intersected = {
  v: number;
} & {};

export type Transformed = Required<Intersected>

generates correct json schema:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$ref": "#/definitions/Transformed",
  "definitions": {
    "Transformed": {
      "type": "object",
      "properties": {
        "v": {
          "type": "number",
        },
      },
      "additionalProperties": false,
    },
  },
}

Clues

Interestingly, if we remove the jsdoc comment, we get a generated schema that
may provide some insight into what is happening:

type Intersected = {
  v: number[]; // no jsdoc comment
} & {};

export type Transformed = Required<Intersected>

compiles to:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$ref": "#/definitions/Transformed",
  "definitions": {
    "Transformed": {
      "type": "object",
      "properties": {
        "v": {
          "allOf": [
            {
              "type": "array",
              "items": {
                "type": "number",
              },
            }, {
              "type": "object",
              "properties": {},
            }
          ],
        },
      },
      "additionalProperties": false,
    },
  },
}

It appears to be intersecting the child property with an empty object
(instead of the parent).

Repro

const config: Config = {
  ...DEFAULT_CONFIG,
  additionalProperties: true,
  path: "test.ts", // above examples
  skipTypeCheck: true,
  tsconfig: "tsconfig.json", // identical to the one used by the `ts-json-schema-generator` package
};
const schema = createGenerator(config).createSchema(config.type);

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions