Skip to content

Commit

Permalink
Revision 0.32.0
Browse files Browse the repository at this point in the history
  • Loading branch information
sinclairzx81 committed Nov 29, 2023
1 parent 4e85569 commit b8ea289
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 47 deletions.
62 changes: 22 additions & 40 deletions examples/index.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,25 @@
import Type, { type Static } from '@sinclair/typebox'
import Type, { FormatRegistry, type Static } from '@sinclair/typebox'
import { Value } from '@sinclair/typebox/value'
import { TypeCompiler } from '@sinclair/typebox/compiler'

const A = Type.Number({})

// console.log(AA)

// const X = TypeCompiler.Code(
// Type.Object({
// x: Type.String(),
// y: Type.BigInt(),
// z: Type.Array(Type.Null()),
// }),
// )

// console.log(X)

// const A = Type.Transform(Type.String())
// .Decode((value: string): Date => new Date(value))
// .Encode((value: Date): string => value.toISOString())

// const B = Type.Union([Type.Object({ date: A }), Type.Number()])

// const C = Type.Transform(B)
// .Decode((value) => {
// console.log(value)
// if (typeof value === 'object') {
// // date is supposed to be a Date according to type inference, but it's a string when it's logged.
// console.log('union', typeof value.date) // string
// }
// return value
// })
// .Encode((o) => o)

// const D = Type.Transform(Type.Object({ date: A }))
// .Decode((o) => {
// console.log('simple', typeof o.date) // object
// return o
// })
// .Encode((o) => o)

// const R1 = Value.Decode(C, [], { date: new Date().toISOString() })
const A = Type.Transform(Type.String())
.Decode((value: string) => new Date(value))
.Encode((value: Date) => value.toISOString())

const B = Type.Union([Type.Object({ date: A }), Type.Number()])
const T1 = Type.Transform(B)
.Decode((value) => {
// expect date
return value
})
.Encode((value) => value)
const T2 = Type.Transform(B)
.Decode((value) => {
// expect number
console.log(value)
return value
})
.Encode((value) => value)

const R1 = Value.Decode(T1, { date: new Date().toISOString() })
const R2 = Value.Decode(T2, 1)
9 changes: 5 additions & 4 deletions src/value/transform/decode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,13 @@ function TTuple(schema: TTuple, references: TSchema[], value: any) {
}
// prettier-ignore
function TUnion(schema: TUnion, references: TSchema[], value: any) {
const defaulted = Default(schema, value)
for (const subschema of schema.anyOf) {
if (!Check(subschema, references, defaulted)) continue
return Visit(subschema, references, defaulted)
if (!Check(subschema, references, value)) continue
// note: ensure interior is decoded first
const decoded = Visit(subschema, references, value)
return Default(schema, decoded)
}
return defaulted
return Default(schema, value)
}
// prettier-ignore
function Visit(schema: TSchema, references: TSchema[], value: any): any {
Expand Down
6 changes: 5 additions & 1 deletion test/runtime/assert/assert.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import * as assert from 'assert'

export namespace Assert {
export function HasProperty<K extends PropertyKey>(value: unknown, key: K): asserts value is Record<K, unknown> {
if (typeof value === 'object' && value !== null && key in value) return
throw new Error(`Expected value to have property '${key as string}'`)
}
export function IsTrue(value: boolean): asserts value is true {
return assert.strictEqual(value, true)
}
Expand Down Expand Up @@ -46,7 +50,7 @@ export namespace Assert {
if (value instanceof constructor) return
throw Error(`Value is not instance of ${constructor}`)
}
export function IsTypeOf(value: any, type: any) {
export function IsTypeOf<T extends 'string' | 'boolean' | 'number' | 'bigint' | 'symbol' | 'object' | 'function'>(value: any, type: T) {
if (typeof value === type) return
throw Error(`Value is not typeof ${type}`)
}
Expand Down
43 changes: 41 additions & 2 deletions test/runtime/value/transform/union.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as Encoder from './_encoder'
import { Assert } from '../../assert'
import { Value } from '@sinclair/typebox/value'
import { Type, TSchema } from '@sinclair/typebox'
import { Type } from '@sinclair/typebox'

describe('value/transform/Union', () => {
// --------------------------------------------------------
Expand Down Expand Up @@ -199,4 +198,44 @@ describe('value/transform/Union', () => {
it('Should throw on interior union encode', () => {
Assert.Throws(() => Encoder.Encode(T52, 1))
})
// prettier-ignore
{ // https://github.com/sinclairzx81/typebox/issues/676
// interior-type
const S = Type.Transform(Type.String())
.Decode((value: string) => new globalThis.Date(value))
.Encode((value: Date) => value.toISOString())
// union-type
const U = Type.Union([
Type.Object({ date: S }),
Type.Number()
])
// expect date on decode
const T1 = Type.Transform(U)
.Decode((value) => {
Assert.IsTypeOf(value, 'object')
Assert.HasProperty(value, 'date')
Assert.IsInstanceOf(value.date, globalThis.Date);
return value
})
.Encode((value) => value)
// expect number on decode
const T2 = Type.Transform(U)
.Decode((value) => {
Assert.IsTypeOf(value, 'number')
return value
})
.Encode((value) => value)

it('Should decode interior union 1', () => {
const R = Encoder.Decode(T1, { date: new globalThis.Date().toISOString() })
Assert.IsTypeOf(R, 'object')
Assert.HasProperty(R, 'date')
Assert.IsInstanceOf(R.date, globalThis.Date);
})
it('Should decode interior union 2', () => {
const R = Encoder.Decode(T2, 123)
Assert.IsTypeOf(R, 'number')
Assert.IsEqual(R, 123)
})
}
})

0 comments on commit b8ea289

Please sign in to comment.