From 95d5a3e5a86110c4690b470d77e6330e8bcb9e91 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 24 May 2024 13:56:58 +0900 Subject: [PATCH] Revision 0.32.31 (#881) * Deref Union Variants on Cast * Version --- package-lock.json | 4 ++-- package.json | 2 +- src/value/cast/cast.ts | 5 +++-- src/value/deref/deref.ts | 17 ++++++++++++----- test/runtime/value/cast/union.ts | 16 ++++++++++++++++ 5 files changed, 34 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index ee68e06..33b9131 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.30", + "version": "0.32.31", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.30", + "version": "0.32.31", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index af35234..8ae41d1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.30", + "version": "0.32.31", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/cast/cast.ts b/src/value/cast/cast.ts index 999e937..c3313f7 100644 --- a/src/value/cast/cast.ts +++ b/src/value/cast/cast.ts @@ -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 diff --git a/src/value/deref/deref.ts b/src/value/deref/deref.ts index c51fc23..91a7791 100644 --- a/src/value/deref/deref.ts +++ b/src/value/deref/deref.ts @@ -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}'`) } } -/** 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] +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: TSchema, references: TSchema[]): TSchema { + // prettier-ignore + return (schema[Kind] === 'This' || schema[Kind] === 'Ref') + ? Resolve(schema as never, references) + : schema } diff --git a/test/runtime/value/cast/union.ts b/test/runtime/value/cast/union.ts index 33d77b4..d9ae730 100644 --- a/test/runtime/value/cast/union.ts +++ b/test/runtime/value/cast/union.ts @@ -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' }) + }) })