Using Value.Convert with string source data to union containing string and non-string? #1297
Replies: 1 comment
-
@colatkinson Hi,
As a general rule, TypeBox expects types to describe values exactly as an application would like to receive them, but it doesn't want to know the state in which values could be sent (which could be anything) In the case you presented, you defined a const TExample = Type.Object({
stringFirst: Type.String(), // remove unions. Just define the exact type
numberFirst: Type.Number(), // you want your application to receive.
})
const converted = Value.Convert(TExample, { stringFirst: "123", numberFirst: "123" })
console.log(JSON.stringify(converted, undefined, 4))
// {
// "stringFirst": "123",
// "numberFirst": 123
// } There are of course cases where an application may need to receive structures that are Union, in which case TypeBox uses the Left to Right ordering to Test and Convert, returning the first matching if it can. The update on #1282 largely corrected union handling inline with this (which I think is correct)
I don't think it's a bug. Historically TypeBox has preferred value conversion to be an opaque process that either returns a valid value (if conversion was possible) or invalid (if a conversion was not possible). An application should trust that TypeBox has made an adequate attempt to convert a value, but where the value returned may still be entirely invalid (failed conversion) Input -> Convert -> Check // The return value of Convert MUST be checked TypeBox prefers Convert to be opaque for the following reasons:
The lack of documentation to the internal processes of Convert is an unfortunate a consequence of the evolving nature of the function. This said, TypeBox loosely follows AJV coercion rules. https://ajv.js.org/coercion.html
Of course, this only accounts for primitive types. When considering deeply nested / logical (OR / AND) / structural schematics, the Convert function sits somewhere between real world pragmatism and academia. I'm using the former to try shed light on the latter, but given the myriad of ways users want to see values converted (especially union), it may forever remain in the former. We'll see. Hope this brings some insight into the Convert function and some of the thinking around it. With respect to using another library to handle conversion, if it does what you need, that's the way to go. I will say, I am looking at ways to make the value processing system more flexible in the long term, including letting users build and integrate their own processing functions (i.e. BYO Convert function) and wire them up into pipelines. Current design is loosely the following. // ------------------------------------------------------------------
// Standard Parse Pipeline
// ------------------------------------------------------------------
const Parse = Value.Pipeline([
(_context, _type, value) => Value.Clone(value),
(context, type, value) => Value.Clean(context, type, value),
(context, type, value) => Value.Default(context, type, value),
(context, type, value) => Value.Convert(context, type, value), // <- replace with your own function here
(context, type, value) => { Value.Assert(context, type, value); return value },
])
const parsed = Parse({}, Type.String(), 'hello world') I will be introducing some of this infrastructure in the next version scheduled for next month. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Suppose we have some code like the following:
Between 0.34.37 and 0.34.38, the behavior changed due to #1282.
With 0.34.37:
With 0.34.38:
I'd hesitate to call this a bug or regression in any way, since I don't think this left-to-right union evaluation was documented in any way. If this sort of thing just isn't something the project wants to support/deal with, that's absolutely understandable. If this is out of scope for
Value.Convert
, switching to an alternative validator for this particular use case is totally feasible.To add some details, the specific use case I'm looking at is loading data from env vars -- thus the source values will always be strings.
Is there a way to denote the source values as "strings-but-not-strings", for example by making a wrapper class with a custom conversion? The current conversion functions seem fairly static, so doesn't seem to be support for such a thing but maybe I'm missing something?
typebox/src/value/convert/convert.ts
Lines 121 to 126 in c54a0f8
Alternatively, could it make sense to allow explicitly marking certain union members as preferable? Or perhaps flagging some unions as left-to-right?
Beta Was this translation helpful? Give feedback.
All reactions