Skip to content

Duplicate (nested) discriminator union values not allowed #1750

Open
@maxboone

Description

@maxboone

Take, for example, a collection of pets:

type RedCat = { color: "red" };
type AshCat = { color: "ash" };

/**
 * @discriminator color
 */
type Cat = AshCat | RedCat;

/**
 * @discriminator animal
 */
type Pet = (
    ({ animal: "cat" } & Cat)
    | ({ animal: "dog" })
);

This yields an error that there are "Duplicate discriminator values: cat", which makes sense, as there are now two elements that match the "animal" tag. Not passing a discriminator will yield a (fairly flat) JSON Schema, but I like using the if-else statements for better error messages and more readability of the schema.

I'd expect something like the following to be possible to generate, by extending the current logic:

{
  "$ref": "#/definitions/Pet",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "Pet": {
      "allOf": [
        {
          "if": {
            "properties": {
              "animal": {
                "const": "cat",
                "type": "string"
              }
            }
          },
          "then": {
            "$ref": "#/definitions/Cat"
          }
        },
        {
          "if": {
            "properties": {
              "animal": {
                "const": "dog",
                "type": "string"
              }
            }
          },
          "then": {
            "$ref": "#/definitions/Dog"
          }
        }
      ],
      "properties": {
        "animal": {
          "type": "string",
          "enum": ["dog", "cat"]
        }
      },
      "required": ["animal"],
      "type": "object"
    },
    "Dog": {
      "type": "object"
    },
    "Cat": {
      "allOf": [
        {
          "if": {
            "properties": {
              "color": {
                "const": "red",
                "type": "string"
              }
            }
          },
          "then": {
            "$ref": "#/definitions/RedCat"
          }
        },
        {
          "if": {
            "properties": {
              "color": {
                "const": "ash",
                "type": "string"
              }
            }
          },
          "then": {
            "$ref": "#/definitions/AshCat"
          }
        }
      ],
      "properties": {
        "color": {
          "type": "string",
          "enum": ["ash", "red"]
        }
      },
      "required": ["color"],
      "type": "object"
    },
    "RedCat": {
      "type": "object"
    },
    "AshCat": {
      "type": "object"
    }
  }
}

I would, in this case, add some recursive logic that also applies the if-then constants to the leafs so that they can disable additional properties for better type checking.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions