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
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:
Run this using
ts-nodeor compile and run it via node. The output I get isWhen I specify
actionType: 'UPLOAD', I expect that the only missing fields shouldurlandfilename. 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
actionTypefield is set to one ofEMAILorUPLOAD.Here are my versions via
yarn list: