Skip to content

Commit

Permalink
Deref Union Variants on Cast
Browse files Browse the repository at this point in the history
  • Loading branch information
sinclairzx81 committed May 24, 2024
1 parent 24c8639 commit 77ca6f5
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 6 deletions.
5 changes: 3 additions & 2 deletions src/value/cast/cast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,9 @@ function ScoreUnion(schema: TSchema, references: TSchema[], value: any): number
}
}
function SelectUnion(union: TUnion, references: TSchema[], value: any): TSchema {
let [select, best] = [union.anyOf[0], 0]
for (const schema of union.anyOf) {
const schemas = union.anyOf.map((schema) => Deref(schema, references))
let [select, best] = [schemas[0], 0]
for (const schema of schemas) {
const score = ScoreUnion(schema, references, value)
if (score > best) {
select = schema
Expand Down
15 changes: 11 additions & 4 deletions src/value/deref/deref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,22 @@ import type { TSchema } from '../../type/schema/index'
import type { TRef } from '../../type/ref/index'
import type { TThis } from '../../type/recursive/index'
import { TypeBoxError } from '../../type/error/index'
import { Kind } from '../../type/symbols/index'

export class TypeDereferenceError extends TypeBoxError {
constructor(public readonly schema: TRef | TThis) {
super(`Unable to dereference schema with $id '${schema.$id}'`)
}
}
function Resolve(schema: TThis | TRef, references: TSchema[]): TSchema {
const target = references.find((target) => target.$id === schema.$ref)
if (target === undefined) throw new TypeDereferenceError(schema)
return Deref(target, references)
}
/** Dereferences a schema from the references array or throws if not found */
export function Deref(schema: TRef | TThis, references: TSchema[]): TSchema {
const index = references.findIndex((target) => target.$id === schema.$ref)
if (index === -1) throw new TypeDereferenceError(schema)
return references[index]
export function Deref(schema: TSchema, references: TSchema[]): TSchema {
// prettier-ignore
return (schema[Kind] === 'This' || schema[Kind] === 'Ref')
? Resolve(schema as never, references)
: schema
}
16 changes: 16 additions & 0 deletions test/runtime/value/cast/union.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,20 @@ describe('value/cast/Union', () => {
value: 'B',
})
})
// ----------------------------------------------------------------
// https://github.com/sinclairzx81/typebox/issues/880
// ----------------------------------------------------------------
// prettier-ignore
it('Should dereference union variants', () => {
const A = Type.Object({ type: Type.Literal('A') }, { $id: 'A' })
const B = Type.Object({ type: Type.Literal('B'), value: Type.Number() }, { $id: 'B' })
const RA = Type.Union([A, B])
const RB = Type.Union([Type.Ref(A), Type.Ref(B)])
// variant 0
Assert.IsEqual(Value.Cast(RA, [A, B], { type: 'B' }), { type: 'B', value: 0 })
Assert.IsEqual(Value.Cast(RB, [A, B], { type: 'B' }), { type: 'B', value: 0 })
// variant 1
Assert.IsEqual(Value.Cast(RA, [A, B], { type: 'A' }), { type: 'A' })
Assert.IsEqual(Value.Cast(RB, [A, B], { type: 'A' }), { type: 'A' })
})
})

0 comments on commit 77ca6f5

Please sign in to comment.