Open AndreiCravtov opened 2 months ago
It seems that the type check fails when a generic schema is involved. Unfortunately, I do not have a solution for you at the moment. The only workaround for now is to cast the type and ignore the TS errors.
import * as v from 'valibot';
const ResultSchema = <
DataSchema extends v.GenericSchema,
ErrorSchema extends v.GenericSchema,
>(
dataSchema: DataSchema,
errorSchema: ErrorSchema
) =>
v.variant('success', [
// @ts-expect-error
v.object({ success: v.literal(true), data: dataSchema }),
// @ts-expect-error
v.object({ success: v.literal(false), error: errorSchema }),
]) as v.VariantSchema<
'success',
// @ts-expect-error
[
v.ObjectSchema<
{ success: v.LiteralSchema<true, undefined>; data: DataSchema },
undefined
>,
v.ObjectSchema<
{ success: v.LiteralSchema<false, undefined>; data: ErrorSchema },
undefined
>,
],
undefined
>;
I see, thank-you. I asked this purely out of curiosity, but there just seems to be an enigmatic type-error deep-down that isn't yet explainable.
I may look into this at a later date, but I am honestly not sure if this is fixable.
I'm excited to switch from zod to valibot, since the former is too bloated for my use case, and where Valibot's variant is composable, zod's discriminatedUnion isn't.
Anyway, this is a bit of a blocker issue for me, as I'm not seeing what I could cast my schema to.
With zod I had this:
const baseLogicalOperation = idObject.extend({
type: z.literal("LogicalOperation"),
operator: z.enum(["and", "or"]),
negated: z.boolean(),
});
export type LogicalOperation = z.infer<typeof baseLogicalOperation> & {
conditions: Condition[];
};
const logicalOperation = baseLogicalOperation.extend({
conditions: z.lazy(() => z.array(condition)),
}) as z.ZodType<LogicalOperation>;
With vali I'm trying
export type LogicalOperation = {
id: string;
type: "LogicalOperation";
operator: "and" | "or";
negated: boolean;
conditions: Condition[];
};
const logicalOperation: v.GenericSchema<LogicalOperation> = v.object({
...idObject.entries,
type: v.literal("LogicalOperation"),
operator: v.picklist(["and", "or"]),
negated: v.boolean(),
conditions: v.lazy(() => v.array(condition)),
});
export const condition = v.variant("type", [
answerComparison,
logicalOperation,
]);
where I don't love the field repetition, though I could live with it. The bigger issue is that I can indeed not use GenericSchema in the variant.
Does this work for you?
import * as v from 'valibot';
type Condition = {
// ...
};
const answerComparison = v.object({
type: v.literal('AnswerComparison'),
});
const logicalOperation = v.object({
type: v.literal('LogicalOperation'),
operator: v.picklist(['and', 'or']),
negated: v.boolean(),
conditions: v.lazy(() => v.array(condition)),
});
const condition: v.GenericSchema<any> = v.variant('type', [
answerComparison,
logicalOperation,
]);
Thanks for the quick reply! Initially I was skeptical because of any
, but then it did lead me to play around with it some more, and I this version here now works for me:
export type LogicalOperation = {
id: string;
type: "LogicalOperation";
operator: "and" | "or";
negated: boolean;
conditions: Condition[];
};
const logicalOperation = v.object({
...idObject.entries,
type: v.literal("LogicalOperation"),
operator: v.picklist(["and", "or"]),
negated: v.boolean(),
conditions: v.lazy((): v.GenericSchema<Condition[]> => v.array(condition)),
});
export const condition = v.variant("type", [
answerComparison,
logicalOperation,
]);
export type Condition = LogicalOperation | AnswerComparison;
I'm still a bit hesitant to make the switch because I now have a number of type definitions that repeat what schemas already state and I'm worried I'll make a mistake and the two will desync. But I think I'll risk it nonetheless, because of bundle size and the beauty of nested variant
s. Thank you!
any
was an accident. I had planned to put Condition
in there. My bad.
I am trying to make a schema-constructor corresponding to a Rust-style
Result
type, more concretely it has this type:I can make a schema-constructor like this:
And that works, but this is a discriminated union so
variant
would be more appropriate. However when I try to use itI get these type errors
Not sure what any of this means, but I'm suspecting there is some small detail I missed or some generic I need to use. Now, I can just use
union
but I am curious whyvariant
doesn't work.