`unevaluatedProperties` failed verification when combined with `Type.Intersect` and `Type.Union`
First of all, thank you very much. Recently, there is a requirement similar to { a: string } & ({ b: string } | { c: string }):
TypeBox version: 0.32.31
import { Type, type Static } from '@sinclair/typebox'
import { Value } from '@sinclair/typebox/value'
import Ajv2019 from 'ajv/dist/2019'
export type T = Static<typeof T>
const T = Type.Intersect(
[
Type.Object({ a: Type.String() }),
Type.Union([
Type.Object({ b: Type.String() }),
Type.Object({ c: Type.String() })
])
],
{ unevaluatedProperties: false }
)
const obj: T = { a: 'a', b: 'b' }
console.log(new Ajv2019().validate(T, obj)) // true
// https://www.jsonschemavalidator.net/ Also passed
console.log(Value.Check(T, obj)) // false, "Unexpected property"
/* The generated JsonScheme itself is not a problem
{
"unevaluatedProperties": false,
"type": "object",
"allOf": [
{
"type": "object",
"properties": { "a": { "type": "string" } },
"required": ["a"]
},
{
"anyOf": [
{
"type": "object",
"properties": { "b": { "type": "string" } },
"required": ["b"]
},
{
"type": "object",
"properties": { "c": { "type": "string" } },
"required": ["c"]
}
]
}
]
}
*/
Finally, thank you again!
@eygsoft Hi! Thanks for reporting!
Yes, this looks like a bug. At this stage, I don't think I'll be able to take a look into the issue until the weekend at the earliest (as I am currently tied up with some other work), however you may be able to workaround this issue if you can reshape the schematics into the following.
const A = Type.Object({
a: Type.String(),
b: Type.String()
}, { additionalProperties: false })
const B = Type.Object({
a: Type.String(),
c: Type.String()
}, { additionalProperties: false })
const T = Type.Union([A, B]) // T = { a: string, b: string } | { a: string, c: string }
This issue actually crosses over with some other areas of the library where I'm looking to implement a distributive evaluate similar to the following....
type T = (
{ x: number }
) & (
{ y: number } |
{ z: number }
)
// non-distributive: where passing y | z is observed as unevaluated properties
type A = {[K in keyof T]: T[K]} & {} // type A = { x: number }
// distributive: where y | z are distributed with outer property x
type E<T> = {[K in keyof T]: T[K]} & {}
type B = E<T> // type B = { x: number; y: number; } |
// { x: number; z: number; }
Note that TypeBox observes the validating schematic as A, where it should be observed as B. The validator should apply distributive union rules to the schematics (which has been challenging at a type level, but may be more tenable at a validation level)
Will keep you posted on any changes here. Cheers S
@eygsoft Hi! Thanks for reporting!
Yes, this looks like a bug. At this stage, I don't think I'll be able to take a look into the issue until the weekend at the earliest (as I am currently tied up with some other work), however you may be able to workaround this issue if you can reshape the schematics into the following.
const A = Type.Object({ a: Type.String(), b: Type.String() }, { additionalProperties: false }) const B = Type.Object({ a: Type.String(), c: Type.String() }, { additionalProperties: false }) const T = Type.Union([A, B]) // T = { a: string, b: string } | { a: string, c: string }This issue actually crosses over with some other areas of the library where I'm looking to implement a distributive evaluate similar to the following....
type T = ( { x: number } ) & ( { y: number } | { z: number } ) // non-distributive: where passing y | z is observed as unevaluated properties type A = {[K in keyof T]: T[K]} & {} // type A = { x: number } // distributive: where y | z are distributed with outer property x type E<T> = {[K in keyof T]: T[K]} & {} type B = E<T> // type B = { x: number; y: number; } | // { x: number; z: number; }Note that TypeBox observes the validating schematic as
A, where it should be observed asB. The validator should apply distributive union rules to the schematics (which has been challenging at a type level, but may be more tenable at a validation level)Will keep you posted on any changes here. Cheers S
Thank you. The method I used is exactly the solution you mentioned, and it works very well. I just discovered this issue and I hope this great project can be more perfect. Thank you again!
@eygsoft Hiya,
Hey, going to close this one out as the workaround sounded like it solved the issue. A lot of this work fits under the umbrella of evaluated types which is due before the end of the year (hopefully). This should provide better options for distributive union types inline with the types mentioned on this issue. Will be good when it lands :)
Will close up for now. All the best! S