bcherny/json-schema-to-typescript

$ref inheritance not supported

Open

#613 aberto em 25 de jul. de 2024

Ver no GitHub
 (11 comments) (6 reactions) (0 assignees)TypeScript (449 forks)github user discovery
bughelp wanted

Métricas do repositório

Stars
 (3.302 stars)
Métricas de merge de PR
 (Nenhuma PRs mesclada em 30d)

Description

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.

Guia do colaborador