bcherny/json-schema-to-typescript

$ref inheritance not supported

Open

#613 aperta il 25 lug 2024

Vedi su GitHub
 (11 commenti) (6 reazioni) (0 assegnatari)TypeScript (449 fork)github user discovery
bughelp wanted

Metriche repository

Star
 (3302 star)
Metriche merge PR
 (Nessuna PR mergiata in 30 g)

Descrizione

When modeling inheritance, we can use $ref to point to an existing definition and extend it.

e.g.:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Sample",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "neighborhood": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/person"
      }
    }
  },
  "$defs": {
    "baseType": {
      "title": "BaseType",
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        }
      }
    },
    "person": {
      "title": "Person",
      "$ref": "#/$defs/baseType",
      "type": "object",
      "unevaluatedProperties": false,
      "properties": {
        "age": {
          "type": "number"
        }
      }
    }
  }
}

and the expected output to be equivalent to:

export interface Sample {
  neighborhood?: Person[];
}
export interface Person extends BaseType {
  age?: "number";
}
export interface BaseType {
  name?: string;
}

But with the provided code, the output of json-schema-to-typescript (14.1) with default options will be erroneous:

  • There won't be any type generated for BaseType even if it's "reachable"
  • Properties from BaseType will be missing in Person
  • Person object can have extra properties even though unevaluatedProperties is set to false

Here is the generated output with the default settings:

export interface Sample {
  neighborhood?: Person[];
}
export interface Person {
  age?: "number";
  [k: string]: unknown;
}

Even by enabling unreachableDefinitions, the output isn't better. BaseType appears but it's still not used properly.

export interface Sample {
  neighborhood?: Person[];
}
export interface Person {
  age?: "number";
  [k: string]: unknown;
}
/**
 * This interface was referenced by `Sample`'s JSON-Schema
 * via the `definition` "baseType".
 */
export interface BaseType {
  name?: string;
  [k: string]: unknown;
}
/**
 * This interface was referenced by `Sample`'s JSON-Schema
 * via the `definition` "person".
 */
export interface Person1 {
  age?: "number";
  [k: string]: unknown;
}

The closest schema that would produce something close to the expected result would be:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Sample",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "neighborhood": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/person"
      }
    }
  },
  "$defs": {
    "baseType": {
      "title": "BaseType",
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        }
      }
    },
    "person": {
      "title": "Person",
      "type": "object",
      "allOf": [
        { "$ref": "#/$defs/baseType" },
        {
          "properties": {
            "age": {
              "type": "number"
            }
          },
          "unevaluatedProperties": false
        }
      ]
    }
  }
}

Edit: I removed a part I was talking about using additionalProperties to store the $ref but I was mistaken, it's not the same behavior.

Guida contributor