json-schema-to-typescript icon indicating copy to clipboard operation
json-schema-to-typescript copied to clipboard

Property type changes from "unknown" to "{[k: string]: unknown}" when adding a description to the schema

Open SimonSimCity opened this issue 3 years ago • 2 comments

When generating typescript types for json-patch as defined at http://json.schemastore.org/json-patch, I found the output slightly suspicious, which I wanted to pick up here.

When I input the following string (where I just added a "type": "object" to the oneOf-items):

{
  "title": "JSON schema for JSONPatch files",
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "array",

  "items": {
    "oneOf": [
      {
        "type": "object",
        "additionalProperties": false,
        "required": ["value", "op", "path"],
        "properties": {
          "path": { "$ref": "#/definitions/path" },
          "op": {
            "description": "The operation to perform.",
            "type": "string",
            "enum": ["add", "replace", "test"]
          },
          "value": {
            "description": "The value to add, replace or test."
          }
        }
      },
      {
        "type": "object",
        "additionalProperties": false,
        "required": ["op", "path"],
        "properties": {
          "path": { "$ref": "#/definitions/path" },
          "op": {
            "description": "The operation to perform.",
            "type": "string",
            "enum": ["remove"]
          }
        }
      },
      {
        "type": "object",
        "additionalProperties": false,
        "required": ["from", "op", "path"],
        "properties": {
          "path": { "$ref": "#/definitions/path" },
          "op": {
            "description": "The operation to perform.",
            "type": "string",
            "enum": ["move", "copy"]
          },
          "from": {
            "$ref": "#/definitions/path",
            "description": "A JSON Pointer path pointing to the location to move/copy from."
          }
        }
      }
    ]
  },
  "definitions": {
    "path": {
      "description": "A JSON Pointer path.",
      "type": "string"
    }
  }
}

I get the following result:

/* tslint:disable */
/**
 * This file was automatically generated by json-schema-to-typescript.
 * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
 * and run json-schema-to-typescript to regenerate this file.
 */

/**
 * A JSON Pointer path.
 */
export type Path = string;
export type JSONSchemaForJSONPatchFiles = (
  | {
      path: Path;
      /**
       * The operation to perform.
       */
      op: "add" | "replace" | "test";
      /**
       * The value to add, replace or test.
       */
      value: {
        [k: string]: unknown;
      };
    }
  | {
      path: Path;
      /**
       * The operation to perform.
       */
      op: "remove";
    }
  | {
      path: Path;
      /**
       * The operation to perform.
       */
      op: "move" | "copy";
      /**
       * A JSON Pointer path.
       */
      from: string;
    }
)[];

You may notice the property value of the first object - it is generated as if I had defined it as an object with arbitrary properties of unknown type. Now, when I remove the property description of its definition (the resulting definition is here):

{
  "title": "JSON schema for JSONPatch files",
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "array",

  "items": {
    "oneOf": [
      {
        "type": "object",
        "additionalProperties": false,
        "required": ["value", "op", "path"],
        "properties": {
          "path": { "$ref": "#/definitions/path" },
          "op": {
            "description": "The operation to perform.",
            "type": "string",
            "enum": ["add", "replace", "test"]
          },
          "value": {}
        }
      },
      {
        "type": "object",
        "additionalProperties": false,
        "required": ["op", "path"],
        "properties": {
          "path": { "$ref": "#/definitions/path" },
          "op": {
            "description": "The operation to perform.",
            "type": "string",
            "enum": ["remove"]
          }
        }
      },
      {
        "type": "object",
        "additionalProperties": false,
        "required": ["from", "op", "path"],
        "properties": {
          "path": { "$ref": "#/definitions/path" },
          "op": {
            "description": "The operation to perform.",
            "type": "string",
            "enum": ["move", "copy"]
          },
          "from": {
            "$ref": "#/definitions/path",
            "description": "A JSON Pointer path pointing to the location to move/copy from."
          }
        }
      }
    ]
  },
  "definitions": {
    "path": {
      "description": "A JSON Pointer path.",
      "type": "string"
    }
  }
}

... the result looks as I would've expected it:

/* tslint:disable */
/**
 * This file was automatically generated by json-schema-to-typescript.
 * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
 * and run json-schema-to-typescript to regenerate this file.
 */

/**
 * A JSON Pointer path.
 */
export type Path = string;
export type JSONSchemaForJSONPatchFiles = (
  | {
      path: Path;
      /**
       * The operation to perform.
       */
      op: "add" | "replace" | "test";
      value: unknown;
    }
  | {
      path: Path;
      /**
       * The operation to perform.
       */
      op: "remove";
    }
  | {
      path: Path;
      /**
       * The operation to perform.
       */
      op: "move" | "copy";
      /**
       * A JSON Pointer path.
       */
      from: string;
    }
)[];

Is this a bug? Looks like it to me, since the pre- or absense of a description on a property shouldn't change its type, as of my understanding. I would like to have the description in place while still keeping it as type unknown.

SimonSimCity avatar Jan 13 '22 12:01 SimonSimCity

This is a bug, and I agree with the proposed behavior. We should also update empty schemas (see test/e2e/emptySchema.ts) to emit:

// Expected
type EmptySchema = unknown

// Actual
export interface EmptySchema {
  [k: string]: unknown;
}

bcherny avatar May 22 '22 04:05 bcherny