diff --git a/package-lock.json b/package-lock.json index 9869d6ff3..12347b661 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.17", + "version": "0.31.18", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.17", + "version": "0.31.18", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.18.0", diff --git a/package.json b/package.json index d3e7e5d90..b3a03d866 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.17", + "version": "0.31.18", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/transform.ts b/src/value/transform.ts index 12faf5834..281917f18 100644 --- a/src/value/transform.ts +++ b/src/value/transform.ts @@ -403,11 +403,18 @@ export namespace EncodeTransform { return IsArray(schema.items) ? schema.items.map((schema, index) => Visit(schema, references, value1[index])) : [] } function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any) { + // test value against union variants for (const subschema of schema.anyOf) { if (!checkFunction(subschema, references, value)) continue const value1 = Visit(subschema, references, value) return Default(schema, value1) } + // test transformed value against union variants + for (const subschema of schema.anyOf) { + const value1 = Visit(subschema, references, value) + if (!checkFunction(schema, references, value1)) continue + return Default(schema, value1) + } return Default(schema, value) } function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): any { diff --git a/test/runtime/value/transform/union.ts b/test/runtime/value/transform/union.ts index 993c87002..d1a9cebc5 100644 --- a/test/runtime/value/transform/union.ts +++ b/test/runtime/value/transform/union.ts @@ -1,6 +1,6 @@ import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' -import { Type } from '@sinclair/typebox' +import { Type, TSchema } from '@sinclair/typebox' describe('value/transform/Union', () => { // -------------------------------------------------------- @@ -126,11 +126,10 @@ describe('value/transform/Union', () => { const N41 = Type.Transform(Type.Number()) .Decode((value) => value + 1) .Encode((value) => value - 1) - const N42 = Type.Transform( - Type.Object({ - x: Type.Number(), - }), - ) + // prettier-ignore + const N42 = Type.Transform(Type.Object({ + x: Type.Number() + })) .Decode((value) => ({ x: value.x + 1 })) .Encode((value) => ({ x: value.x - 1 })) const N43 = Type.Transform(Type.Tuple([Type.Number()])) @@ -167,4 +166,36 @@ describe('value/transform/Union', () => { it('Should throw on mixed types decode', () => { Assert.Throws(() => Value.Decode(T4, null)) }) + // -------------------------------------------------------- + // Interior Union Transform + // + // https://github.com/sinclairzx81/typebox/issues/631 + // -------------------------------------------------------- + const T51 = Type.Transform(Type.String()) + .Decode((value) => new Date(value)) + .Encode((value) => value.toISOString()) + const T52 = Type.Union([Type.Null(), T51]) + it('Should decode interior union 1', () => { + const R = Value.Decode(T52, null) + Assert.IsEqual(R, null) + }) + it('Should decode interior union 2', () => { + const R = Value.Decode(T52, new Date().toISOString()) + Assert.IsInstanceOf(R, Date) + }) + it('Should encode interior union 1', () => { + const R = Value.Encode(T52, null) + Assert.IsEqual(R, null) + }) + it('Should encode interior union 2', () => { + const D = new Date() + const R = Value.Encode(T52, D) + Assert.IsEqual(R, D.toISOString()) + }) + it('Should throw on interior union decode', () => { + Assert.Throws(() => Value.Decode(T52, {})) + }) + it('Should throw on interior union encode', () => { + Assert.Throws(() => Value.Encode(T52, 1)) + }) })