ajv icon indicating copy to clipboard operation
ajv copied to clipboard

Type coercion does not work with anyOf Number or Boolean

Open SeanReece opened this issue 2 years ago • 2 comments

TL;DR: I want a property to support either a boolean OR a number value. With coercion enabled, true will be coerced to 1 or 1 will be coerced to true depending on the order of the types in the anyOf.

What version of Ajv are you using? Does the issue happen if you use the latest version?

0.8.12

Ajv options object

const ajv = new Ajv({ coerceTypes: true })

JSON Schema

const schema = {
  type: 'object',
  properties: {
    foo: { anyOf: [{ type: 'boolean' }, { type: 'number' }] },  // Define boolean first
    bar: { anyOf: [{ type: 'number' }, { type: 'boolean' }] },  // Define number first
  },
  required: ['foo'],
  additionalProperties: false,
}

Sample data

const boolData = { foo: true, bar: true } // Results in { foo: true, bar: 1 } 
const numData = { foo: 1, bar: 1 } // results in { foo: true, bar: 1 }

What results did you expect?

const boolData = { foo: true, bar: true } // I expect { foo: true, bar: true } 
const numData = { foo: 1, bar: 1 } // I expect { foo: 1, bar: 1 }

Since coercion is done according to the order of the types in the schema, the resulting coerced type depends on whether boolean or number comes first, BUT I believe both results are incorrect.

The suggestion from this issue: https://github.com/ajv-validator/ajv/issues/399 would have allowed us to fine tune coercion for this use case, but unfortunately I don't believe the suggestion to use keywords helps us in this case. I tried something similar to what was suggested in the comment in that issue but AJV still coerces the value after the keyword.

There must be something I'm missing here. This seems like a fairly common use case that seems broken. Please advise.

SeanReece avatar Nov 20 '23 20:11 SeanReece

Type coercion is an attempt to apply imperative feature, that depends on order, to declarative schema. So you will indeed be getting different results for different order of coercions, and the options are to expect it or to use type with array of strings instead of anyOf.

epoberezkin avatar Jan 17 '24 16:01 epoberezkin

Hi @epoberezkin. Thanks for the response. My example here was purposefully simple just to get the point across but our implementation is quite complex, so doing something with an array of strings wouldn't really work. I mostly just wanted validation that keywords do not allow customizing coercion (as suggested in #399) as coercion will still be run after the keyword logic. Our solution for now is to switch to any in our fastify request schema, then later check what the type is supposed to be and do manual validation/coercion against a single type.

So we have found a solution for us, but I would still argue that having overlapping coercion rules with no way to customize the coercion is an issue.

SeanReece avatar Jan 23 '24 19:01 SeanReece

IIRC you can also manage type coercion separately from validation, a solution involving multiple schemas for the two different purposes. In any case I don't see any further action here so I will close it.

jasoniangreen avatar Jul 29 '24 22:07 jasoniangreen