I am currently using superstruct to type and validate an object. This object is returned via an API and then validated and type cast using the superstruct struct.
The structure of the object depends on a list of strings I only have at runtime, an example is below. There can be an arbitrary number of these arbitrary fields but they always have the same structure.
{
parent:
{
thisFieldIsAlwaysHere: number;
arbitraryObject1: { body: number; };
arbitraryObject2: { body: number; };
arbitraryObject3: { body: number; };
}
}
For this I use a struct with a record with a string for the name and the object for the body of each of these which are the same like so
const Struct = type({
parent: record(string(), type({ body: number() }))
})
This is fine with typescripts types when using infer to cast. But my object fails to validate due to thisFieldIsAlwaysHere
being a number not an object. Including thisFieldIsAlwaysHere
using an intersection does not help. It appears the record is applied to every field.
const Struct = type({
parent: intersection([
type({ thisFieldIsAlwaysHere: number()),
record(string(), type({ body: number() }))
])
})
Is there a way to get this to work elegantly? I have tried using dynamic
. I was wondering if there is a way to exclude certain field names from the record? Or potentially exlclude other fields in the type
I am currently using superstruct to type and validate an object. This object is returned via an API and then validated and type cast using the superstruct struct.
The structure of the object depends on a list of strings I only have at runtime, an example is below. There can be an arbitrary number of these arbitrary fields but they always have the same structure.
{
parent:
{
thisFieldIsAlwaysHere: number;
arbitraryObject1: { body: number; };
arbitraryObject2: { body: number; };
arbitraryObject3: { body: number; };
}
}
For this I use a struct with a record with a string for the name and the object for the body of each of these which are the same like so
const Struct = type({
parent: record(string(), type({ body: number() }))
})
This is fine with typescripts types when using infer to cast. But my object fails to validate due to thisFieldIsAlwaysHere
being a number not an object. Including thisFieldIsAlwaysHere
using an intersection does not help. It appears the record is applied to every field.
const Struct = type({
parent: intersection([
type({ thisFieldIsAlwaysHere: number()),
record(string(), type({ body: number() }))
])
})
Is there a way to get this to work elegantly? I have tried using dynamic
. I was wondering if there is a way to exclude certain field names from the record? Or potentially exlclude other fields in the type
1 Answer
Reset to default 1The current way I have gotten around this is by using coerce
. This allows for transformation pre validation. Then when the invalid content is encountered it can be transformed into the correct one. You need to use create
instead of assert
though. The example is
const Struct = type({
parent: record(
string(),
coerce(type({ body: number() }), number(), (value) => ({ body: value })))
})
Unfortunately this still does not answer how to validate a partially dynamic type like this but still provides a work around at least