fabian-hiller / valibot

The modular and type safe schema library for validating structural data 🤖
https://valibot.dev
MIT License
5.88k stars 181 forks source link

Unable to use `lazy()` in `variant()` #588

Open Schleuse opened 3 months ago

Schleuse commented 3 months ago

I'm currently trying to implement a Schema for the following data structure.

{
    "filters": [
        {
            "type": "and",
            "and": [
                {
                    "type": "or",
                    "or": [
                        {
                            "type": "not",
                            "not": [
                                {
                                    "type": "keyword",
                                    "keyword": "blub"
                                },
                                {
                                    "type": "categories",
                                    "categories": [123, 345, 567]
                                }
                            ]
                        },
                        {
                            "type": "group",
                            "group": 123123123
                        }
                    ]
                }
            ]
        }

    ]
}

I'v already written Schemas for the Value-Filters but struggle to assemble them in a tree-like structure in combination with "or", "and" & "not"-Filters.

import * as v from "valibot";

const CategoryFilterSchema = v.object({
  type: v.literal("category"),
  categories: v.array(v.number())
})
const GroupFilterSchema = v.object({
  type: v.literal("group"),
  group: v.number()
})
const KeywordFilterSchema = v.object({
  type: v.literal("keyword"),
  keyword: v.string()
})

const ValueFilters = v.variant('type', [CategoryFilterSchema, GroupFilterSchema, KeywordFilterSchema]);

type AndFilter = {
  type: "and"
  and: Array<NotFilter | AndFilter | OrFilter | v.InferOutput<typeof ValueFilters>>;
};

type OrFilter = {
  type: "or"
  or: Array<NotFilter | AndFilter | OrFilter | v.InferOutput<typeof ValueFilters>>;
};

type NotFilter = {
  type: "not"
  not: Array<NotFilter | AndFilter | OrFilter | v.InferOutput<typeof ValueFilters>>;
};

const AndFilterSchema: v.GenericSchema<AndFilter> = v.object({
  type: v.literal('and'),
  and: v.array(v.variant('type', [
    ValueFilters,
    v.lazy(() => AndFilterSchema),
    v.lazy(() => OrFilterSchema),
    v.lazy(() => NotFilterSchema),
  ])),
})

const OrFilterSchema: v.GenericSchema<OrFilter> = v.object({
  type: v.literal('or'),
  or: v.array(v.variant('type', [
    ValueFilters,
    v.lazy(() => AndFilterSchema),
    v.lazy(() => OrFilterSchema),
    v.lazy(() => NotFilterSchema),
  ])),
})

const NotFilterSchema: v.GenericSchema<NotFilter> = v.object({
  type: v.literal('not'),
  not: v.array(v.variant('type', [
    ValueFilters,
    v.lazy(() => AndFilterSchema),
    v.lazy(() => OrFilterSchema),
    v.lazy(() => NotFilterSchema),
  ])),
})

const AvailableFilters = v.variant('type', [
  ValueFilters,
  AndFilterSchema,
  OrFilterSchema,
  NotFilterSchema
]);

const Schema = v.object({
  filters: v.array(AvailableFilters)
})

const parsed = v.parse(Schema, {
  "filters": [
    {
      "type": "and",
      "and": [
        {
          "type": "or",
          "or": [
            {
              "type": "not",
              "not": [
                {
                  "type": "keyword",
                  "keyword": "blub"
                },
                {
                  "type": "categories",
                  "categories": [123, 345, 567]
                }
              ]
            },
            {
              "type": "group",
              "group": 123123123
            }
          ]
        }
      ]
    }
  ]
});
console.log(parsed);

Example


Im generally not sure, if I structured things right - how would a schema for such a recursivly nested data structure look like?

It seems be currently not possible to use lazy within variant: image

Also GenericSchema does not seem to satisfy the requirements for variant() either.

image

Maybe someone has some suggestions how I could improve things.

Thank you very much in advance :star2:

fabian-hiller commented 3 months ago

lazy is not currently supported by variant. I will investigate if it is possible and useful to change this. In the meantime, you can switch to union. See this playground.