glideapps / quicktype

Generate types and converters from JSON, Schema, and GraphQL
https://app.quicktype.io
Apache License 2.0
12.5k stars 1.09k forks source link

Wrong enum name when using union with null #2410

Open bdeneux opened 1 year ago

bdeneux commented 1 year ago

I've an issue when trying to convert a JSON schema file to Typescript types. The JSON schema comes from a Rust models that has been converted to JSON Schema.

Given this schema : (I've simplified the schema for this exemple)

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "ConstructResponse",
  "description": "Represents the response of a [QueryMsg::Construct] query.",
  "type": "object",
  "required": [
    "format"
  ],
  "properties": {
    "format": {
      "description": "The format of the data.",
      "anyOf": [
        {
          "$ref": "#/definitions/DataFormat"
        },
        {
           "type": "null"
        }
      ]
    }
  },
  "additionalProperties": false,
  "definitions": {
    "DataFormat": {
      "title": "DataFormat",
      "description": [...],
      "oneOf": [
        {
          "title": "Turtle",
          "description": [...]
          "type": "string",
          "enum": [
            "turtle"
          ]
        },
        {
          "title": "RDF XML",
          "description": [...],
          "type": "string",
          "enum": [
            "rdf_xml"
          ]
        },
        {
          "title": "N-Triples",
          [...]
        },
        {
          "title": "N-Quads",
          [...]
        }
      ]
    }
  }
}

And running QuickType to convert this json schema in Typescript types,

quicktype -s schema schema.json -o test.ts --prefer-type

The conversion use the first enum case as the name of the enum, here the enum will be named Turtle instead of DataFormat.

/**
 * Represents the response of a [QueryMsg::Construct] query.
 */
export type Test = {
    format: Turtle | null;
}

export enum Turtle {
    NQuads = "n_quads",
    NTriples = "n_triples",
    RDFXML = "rdf_xml",
    Turtle = "turtle",
}
Expected result ```ts /** * Represents the response of a [QueryMsg::Construct] query. */ export type Test = { format: DataFormat | null; } export enum DataFormat { NQuads = "n_quads", NTriples = "n_triples", RDFXML = "rdf_xml", Turtle = "turtle", } ```

When I remove { "type": "null" } from the anyOf property of DataFormat, the enum is named correctly.

JSON without the `null` union ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "ConstructResponse", "description": "Represents the response of a [QueryMsg::Construct] query.", "type": "object", "required": [ "format" ], "properties": { "format": { "description": "The format of the data.", "anyOf": [ { "$ref": "#/definitions/DataFormat" } ] } }, "additionalProperties": false, "definitions": { "DataFormat": { "title": "DataFormat", "description": [...], "oneOf": [ { "title": "Turtle", "description": [...] "type": "string", "enum": [ "turtle" ] }, { "title": "RDF XML", "description": [...], "type": "string", "enum": [ "rdf_xml" ] }, { "title": "N-Triples", [...] }, { "title": "N-Quads", [...] } ] } } } ``` Give a good enum name but is not what I want because DataFormat is optional : ```ts /** * Represents the response of a [QueryMsg::Construct] query. */ export type Test = { format: DataFormat; } export enum DataFormat { NQuads = "n_quads", NTriples = "n_triples", RDFXML = "rdf_xml", Turtle = "turtle", } ```

This null type union has been added because format property can be null since this attribute is optional in rust.

I hope you can help me, I'm wondering if it's an issue in this library for this particular case or if it's the JSON Schema that has been malformed by the rust library.

inferrinizzard commented 6 months ago

Hi @bdeneux, there looks to be 2 issues at play here:

  1. There does indeed seem to be a naming issue here with the Enum, which I will take a look at
  2. For an optional property on the format object, an easier representation would just be to omit the key from the required array - although the anyOf with null is also correct (and might make more sense coming from rust)