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

JSONSchemaType + anyOf corresponding to union over objects cannot correctly determine missing fields #144

Open
alexandervandekleutab opened this issue Dec 29, 2021 · 2 comments

Comments

@alexandervandekleutab
Copy link

Problem description

AJV cannot infer which subtype of a union is being used when creating a union over objects. This might be an issue with AJV core, or just an issue with the error handling.

Below is a simple but complete example of the issue:

import ajvErrors from 'ajv-errors'
import addFormats from 'ajv-formats'
import Ajv, { JSONSchemaType } from 'ajv'

type UploadAction = {
  actionType: 'UPLOAD'
  url: string
  filename: string
}

type EmailAction = {
  actionType: 'EMAIL'
  email: string
}

type Action = UploadAction | EmailAction
type Actions = Array<Action>

const schema: JSONSchemaType<Actions> = {
  type: 'array',
  items: {
    anyOf: [
      {
        type: 'object',
        required: ['actionType', 'url', 'filename'],
        properties: {
          actionType: {
            type: 'string',
            const: 'UPLOAD',
          },
          url: {
            type: 'string',
          },
          filename: {
            type: 'string',
          },
        },
      },
      {
        type: 'object',
        required: ['actionType', 'email'],
        properties: {
          actionType: {
            type: 'string',
            const: 'EMAIL',
          },
          email: {
            type: 'string',
          },
        },
      },
    ],
  },
}

if (require.main === module) {
  const ajv = new Ajv({ allErrors: true })
  ajvErrors(ajv)
  addFormats(ajv)
  const validate = ajv.compile(schema)

  const data: Array<unknown> = [
    {
      actionType: 'UPLOAD',
    },
  ]

  if (validate(data)) {
    console.log(data)
  } else {
    console.log(validate.errors)
  }
}

Run this using ts-node or compile and run it via node. The output I get is

[
  {
    instancePath: '/0',
    schemaPath: '#/items/anyOf/0/required',
    keyword: 'required',
    params: { missingProperty: 'url' },
    message: "must have required property 'url'"
  },
  {
    instancePath: '/0',
    schemaPath: '#/items/anyOf/0/required',
    keyword: 'required',
    params: { missingProperty: 'filename' },
    message: "must have required property 'filename'"
  },
  {
    instancePath: '/0',
    schemaPath: '#/items/anyOf/1/required',
    keyword: 'required',
    params: { missingProperty: 'email' },
    message: "must have required property 'email'"
  },
  {
    instancePath: '/0/actionType',
    schemaPath: '#/items/anyOf/1/properties/actionType/const',
    keyword: 'const',
    params: { allowedValue: 'EMAIL' },
    message: 'must be equal to constant'
  },
  {
    instancePath: '/0',
    schemaPath: '#/items/anyOf',
    keyword: 'anyOf',
    params: {},
    message: 'must match a schema in anyOf'
  }
]

When I specify actionType: 'UPLOAD', I expect that the only missing fields should url and filename. The errors output by AJV are confusing and misleading.

In contrast, the typescript type inference engine is correctly able to deduce the the missing fields once the actionType field is set to one of EMAIL or UPLOAD.

Here are my versions via yarn list:

├─ ajv-errors@3.0.0
├─ ajv-formats@2.1.1
│  └─ ajv@^8.0.0
├─ ajv@8.8.2
│  ├─ fast-deep-equal@^3.1.1
│  ├─ json-schema-traverse@^1.0.0
│  ├─ require-from-string@^2.0.2
│  └─ uri-js@^4.2.2
@dontbesatisfied
Copy link

Does anyone know the solution?

@18yy
Copy link

18yy commented May 16, 2024

How did you solve it?

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

No branches or pull requests

3 participants