Comments (4)
Thank you for your answer.
There was indeed a lack of discriminator in my code. I've changed it to include a discriminator and a default string encoding for non-DiscriminatedDate.
from typebox.
@bhamon Hi, thanks for reporting!
Yeah, this is definitely a bug. I had carried out some optimization work last weekend to improve Encode/Decode object initialization performance, but it appears I'd introduced a bug which wasn't being caught by the test suite.
Have pushed a fix for this on 0.32.28. If you install this version, your repro should work as expected.
Let me know how you go.
Cheers
S
from typebox.
Thank you for your time. It works with the new 0.32.28 version.
I also have a question regarding union encoding: does the encoding strategy always use the first element of the union? Or is there a way to enforce it (with an option maybe)?
For example I've defined a date transform as follow:
import {type StringOptions, type NumberOptions, Type, type StaticEncode} from '@sinclair/typebox';
/** Date encoded as an ISO8601-compliant string */
export const DateTimeStringTransform = (_options: StringOptions = {}) =>
Type.Transform(
Type.String({
..._options,
format: 'date-time'
})
)
.Decode(v => new Date(v))
.Encode(v => v.toISOString());
/** Date encoded as a millisecond timestamp number */
export const DateTimeNumberTransform = (_options: NumberOptions = {}) =>
Type.Transform(Type.Integer(_options))
.Decode(v => new Date(v))
.Encode(v => v.getTime());
/** Compound date codec that accepts both string and number representation */
export const DateTimeTransform = (
_options: {string?: StringOptions; number?: NumberOptions} = {string: {}, number: {}}
) => Type.Union([DateTimeStringTransform(_options.string), DateTimeNumberTransform(_options.number)]);
/* inferred as string|number */
type DateTimeTransformEncode = StaticEncode<ReturnType<typeof DateTimeTransform>>;
export default DateTimeTransform;
The inferred encode type is string | number
. It could be syntactically correct. But at run time it always returns a string
because a choice has to be made regarding the proper encode to apply.
from typebox.
I also have a question regarding union encoding: does the encoding strategy always use the first element of the union? Or is there a way to enforce it (with an option maybe)?
TypeBox will always return the first matching Union variant, but it's the role of the Encode function to return one of those variants. In the case of Date, to be able to conditionally Encode as either number or string, you will need the Decode function to apply a discriminator to the Date object such that Encode function can make an informed decision on how to encode later.
// Bad Decode (lost information about source type)
string -> Date
number -> Date
// Good Decode (union information retained for later Encode)
string -> Date & { type: 'string' }
number -> Date & { type: 'number' }
The following implements a DiscriminatedDate type that achieves the above.
import { Value } from '@sinclair/typebox/value'
import { Type } from '@sinclair/typebox'
// ---------------------------------------------------------
// DiscriminatedDate
// ---------------------------------------------------------
export type DiscriminatedDate = Date & { kind: 'string' | 'number' | ({} & string) }
const DecodeDiscriminatedDate = (value: string | number) => (
Object.defineProperty(new Date(value), 'kind', { value: typeof value })
) as DiscriminatedDate
const EncodeDiscriminatedDate = (value: DiscriminatedDate) => (
value.kind === 'string' ? value.toISOString() :
value.kind === 'number' ? value.getTime() :
value.getTime()
)
const DiscriminatedDate = Type.Transform(Type.Union([Type.Number(),Type.String()]))
.Decode(DecodeDiscriminatedDate)
.Encode(EncodeDiscriminatedDate)
// ---------------------------------------------------------
// String
// ---------------------------------------------------------
{
const D = Value.Decode(DiscriminatedDate, '1970-01-01T00:00:12.345Z')
const E = Value.Encode(DiscriminatedDate, D)
console.log('string:')
console.log(' decode:', D)
console.log(' encode:', E)
}
// ---------------------------------------------------------
// Number
// ---------------------------------------------------------
{
const D = Value.Decode(DiscriminatedDate, 12345)
const E = Value.Encode(DiscriminatedDate, D)
console.log('number:')
console.log(' decode:', D)
console.log(' encode:', E)
}
// ---------------------------------------------------------
// Embedded
// ---------------------------------------------------------
{
const User = Type.Transform(Type.Object({ created_at: DiscriminatedDate }))
.Decode(value => ({ createdAt: value.created_at }))
.Encode(value => ({ created_at: value.createdAt }))
const D = Value.Decode(User, { created_at: '1970-01-01T00:00:12.345Z' })
const E = Value.Encode(User, D)
console.log('embedded:')
console.log(' decode:', D)
console.log(' encode:', E)
}
string:
decode: 1970-01-01T00:00:12.345Z
encode: 1970-01-01T00:00:12.345Z
number:
decode: 1970-01-01T00:00:12.345Z
encode: 12345
embedded:
decode: { createdAt: 1970-01-01T00:00:12.345Z }
encode: { created_at: '1970-01-01T00:00:12.345Z' }
Union transforms are somewhat interesting, but as a general rule, if you are transforming a Union with 10 variants, the Decode function should return a mapping all 10 in such a way the Encode function can later perform the reverse mapping.
Hope this helps, will close off this issue for now
Cheers
S
from typebox.
Related Issues (20)
- Indicate which files don't have "sideEffects" in package.json for improved tree shaking HOT 3
- Type.String().Optional() is accepted on ts-hint / ts-lint, and even build successfully; but of course, "Optional()" does not exists HOT 2
- Dynamic Template Literals can't be Mapped HOT 2
- How should TypeBox types be used with TS type predicates? HOT 2
- TypeCompiler.Compile incorrectly parsing `Record<any, any>` HOT 7
- Error when using Composite with Ref (Unable to dereference schema with $id 'undefined') HOT 3
- Transform type keys in Record HOT 2
- Is it possible to add support to the Type.String additional properties for the enum keyword HOT 4
- Support for async validations? HOT 1
- Transform + Map/Index does not work HOT 4
- Update the error message for required properties HOT 3
- Covariance issue with `TypeCheck<TSchema>` and `TypeCheck<TObject<{}>>` HOT 1
- Default values in nested objects are not generated HOT 2
- Doubt on Record HOT 2
- TypeCompiler compile on Type.Strict(T) - Preflight validation check failed to guard for the given schema HOT 2
- Max call stack size exceeded HOT 1
- Value.Convert() doesn't work with nullable record HOT 1
- Variadic Any Typed function parameters HOT 2
- How to do a cross field validation? HOT 3
- Undefined is incorrectly handled in Diff HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from typebox.