Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

anyOf just for required gives unknown #513

Open
leomp12 opened this issue Feb 21, 2023 · 2 comments
Open

anyOf just for required gives unknown #513

leomp12 opened this issue Feb 21, 2023 · 2 comments

Comments

@leomp12
Copy link

leomp12 commented Feb 21, 2023

About

anyOf may be used on objects to define at least one of these properties is required, but not all of them at the same time, in this case anyOf will be an array of objects with only the required property: Array<{ required: string[] }>. In this case the object properties seems to be being ignored and json2ts outputs { [k: string]: unknown; }[].

I think anyOf should be ignored if it's an array of objects with only the required property.

Or some parameter to force ignore anyOf?

Example

The following schema:

{
  "type": "object",
  "definitions": {
    "picture_size": {
      "type": "object",
      "required": [
        "url"
      ],
      "additionalProperties": false,
      "properties": {
        "url": {
          "type": "string",
          "maxLength": 255,
          "format": "uri",
          "description": "Image link"
        },
        "size": {
          "type": "string",
          "maxLength": 11,
          "pattern": "^[1-9]([0-9]+)?x[1-9]([0-9]+)?$",
          "description": "Image size (width x height) in px, such as 100x50 (100px width, 50px height)"
        },
        "alt": {
          "type": "string",
          "maxLength": 255,
          "description": "Alternative text, HTML alt tag (important for SEO)"
        }
      },
      "description": "Image size variant"
    }
  },
  "properties": {
    "pictures": {
      "type": "array",
      "maxItems": 50,
      "items": {
        "type": "object",
        "additionalProperties": false,
        "anyOf": [
          {
            "required": [
              "normal"
            ]
          },
          {
            "required": [
              "big"
            ]
          },
          {
            "required": [
              "zoom"
            ]
          }
        ],
        "dynamicDefaults": {
          "_id": "objectId"
        },
        "properties": {
          "_id": {
            "type": "string",
            "pattern": "^[a-f0-9]{24}$",
            "description": "Picture ID (ObjectID) [auto]"
          },
          "tag": {
            "type": "string",
            "maxLength": 20,
            "pattern": "^[a-z0-9_]+$",
            "description": "Tag to identify object, use only lowercase letters, digits and underscore"
          },
          "normal": {
            "$ref": "#/definitions/picture_size",
            "description": "Default image size variant"
          },
          "big": {
            "$ref": "#/definitions/picture_size",
            "description": "Image big size variant"
          },
          "zoom": {
            "$ref": "#/definitions/picture_size",
            "description": "Image zoom size variant"
          },
          "small": {
            "$ref": "#/definitions/picture_size",
            "description": "Image small size variant"
          }
        },
        "description": "Image object"
      },
      "description": "List of product images"
    }
  }
}

Is compiled to:

export interface Products {
  pictures?: (
    | {
        [k: string]: unknown;
      }
    | {
        [k: string]: unknown;
      }
    | {
        [k: string]: unknown;
      }
  )[];
}

Removing the anyOf it's correctly compiled to:

export interface Products {
  pictures?: {
    /**
     * Picture ID (ObjectID) [auto]
     */
    _id?: string;
    /**
     * Tag to identify object, use only lowercase letters, digits and underscore
     */
    tag?: string;
    normal?: PictureSize;
    big?: PictureSize1;
    zoom?: PictureSize2;
    small?: PictureSize3;
  }[];
}
/**
 * Default image size variant
 */
export interface PictureSize {
  /**
   * Image link
   */
  url: string;
  /**
   * Image size (width x height) in px, such as 100x50 (100px width, 50px height)
   */
  size?: string;
  /**
   * Alternative text, HTML alt tag (important for SEO)
   */
  alt?: string;
}
/**
 * Image big size variant
 */
export interface PictureSize1 {
  /**
   * Image link
   */
  url: string;
  /**
   * Image size (width x height) in px, such as 100x50 (100px width, 50px height)
   */
  size?: string;
  /**
   * Alternative text, HTML alt tag (important for SEO)
   */
  alt?: string;
}
/**
 * Image zoom size variant
 */
export interface PictureSize2 {
  /**
   * Image link
   */
  url: string;
  /**
   * Image size (width x height) in px, such as 100x50 (100px width, 50px height)
   */
  size?: string;
  /**
   * Alternative text, HTML alt tag (important for SEO)
   */
  alt?: string;
}
/**
 * Image small size variant
 */
export interface PictureSize3 {
  /**
   * Image link
   */
  url: string;
  /**
   * Image size (width x height) in px, such as 100x50 (100px width, 50px height)
   */
  size?: string;
  /**
   * Alternative text, HTML alt tag (important for SEO)
   */
  alt?: string;
}
@bcherny
Copy link
Owner

bcherny commented Feb 23, 2023

Whittling your example down a bit, the bug is:

Input

{
  "title": "Example Schema",
  "type": "object",
  "anyOf": [
    {
      "required": "a"
    },
    {
      "required": "b"
    }
  ],
  "properties": {
    "a": {
      "type": "string"
    },
    "b": {
      "type": "string"
    }
  },
  "additionalProperties": false
}

Output (actual)

export type ExampleSchema = ExampleSchema1 & ExampleSchema2;
export type ExampleSchema1 = {
  [k: string]: unknown;
};

export interface ExampleSchema2 {
  a?: string;
  b?: string;
}

Output (expected)

export type ExampleSchema = ExampleSchema1 & ExampleSchema2;
export type ExampleSchema1 = {a: string} | {b: string};

export interface ExampleSchema2 {
  a?: string;
  b?: string;
}

@velkoborsky
Copy link

velkoborsky commented Nov 15, 2023

Similar situation is in a schema with multiple formats for one attribute.

Example

{
  type: 'object',
    properties: {
      date: {
        type: 'string',
        anyOf: [{ format: 'date' }, { format: 'date-time' }],
      },
  },
}

Output (expected)

date: string;

Output (actual)

date: {
  [k: string]: unknown;
} & string;

Practically, such schemas work fine in e.g., ajv.

There seems to be a workaround for this specific situation. Code below works fine.

Workardound

{
  type: 'object',
    properties: {
      date: {
        anyOf: [
          { type: 'string', format: 'date' },
          { type: 'string', format: 'date-time' },
        ],
      },
  },
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants