valibot icon indicating copy to clipboard operation
valibot copied to clipboard

Request for Enhancement: Support for Multiple Keys in variant Function

Open JolianGof opened this issue 1 year ago • 3 comments

Request for Enhancement: Support for Multiple Keys in variant Function

Hello Valibot Team,

I've been exploring Valibot for schema validation in my project and find it to be an invaluable tool for ensuring data integrity and type safety. However, I've encountered a scenario where I need to validate data based on multiple keys, rather than just one, using the variant function. I understand that the current implementation of the variant function supports validation based on a single discriminator key.

In my application, I'm dealing with complex data structures that require validation based on a combination of keys. Specifically, I need to validate data based on both type and email fields, where the validation schema varies depending on the combination of these two fields. While I'm aware that the variant function can be used to create nested schemas for complex validation scenarios, I've found that this approach significantly increases the complexity of my validation logic, making it more difficult to manage and understand.

Proposed Solution:

I'm proposing that the variant function be enhanced to support validation based on multiple keys. This would allow for more straightforward and readable validation logic, enabling developers to handle complex data structures more effectively without the need for deeply nested schemas.

Here's a conceptual example of how this could be implemented:


const VariantSchema = variant(['type', 'email'], [
  object({
    type: literal('email'),
    email: literal('[email protected]'),
    urls: array(string())
  }),
  object({
    type: literal('url'),
    email: literal('[email protected]'),
    urls: Picklist(['https://', 'https://this.com'])
  }),
  object({
    type: literal('date'),
    email: literal('[email protected]'),
    ips: Picklist(['12.2.2.2', '2.2.2.2']),
    date: string([isoDate()])
  }),
]);

In this example, the variant function would select the appropriate schema based on the combination of type and email fields, rather than just one key. This approach would simplify the validation logic and make it easier to manage complex validation scenarios.

Request:

I kindly request your consideration of this feature request. If there's an existing solution or workaround for this scenario, I would appreciate being directed towards it. Alternatively, if you're considering adding this feature to Valibot, I believe it would greatly enhance the library's capabilities and usability for developers dealing with complex data structures.

Thank you for your time and consideration. I look forward to any feedback or discussions on this matter.

JolianGof avatar Feb 27 '24 18:02 JolianGof

Thank you for creating this issue. What is the difference with multiple keys? It already works the way you want if you specify only one key. Check it out in our playground.

import * as v from 'valibot';

const VariantSchema = v.variant('type', [
  v.object({
    type: v.literal('email'),
    email: v.literal('[email protected]'),
    urls: v.array(v.string([v.url()])),
  }),
  v.object({
    type: v.literal('url'),
    email: v.literal('[email protected]'),
    url: v.picklist(['https://', 'https://this.com']),
  }),
  v.object({
    type: v.literal('date'),
    email: v.literal('[email protected]'),
    ip: v.picklist(['12.2.2.2', '2.2.2.2']),
    date: v.string([v.isoDate()]),
  }),
]);

fabian-hiller avatar Feb 28 '24 17:02 fabian-hiller

I want to apologize for any confusion my previous message may have caused. Upon further reflection, I realize that the example I provided did not accurately represent the complexity of my use case, particularly concerning the handling of null values within the variant schema . n my project, I am dealing with a complex form that has numerous cases, each dependent on specific conditions. These conditions are not just based on a single discriminator key but require a combination of keys to determine the correct validation schema. This complexity is crucial for my application, as it needs to accurately validate data based on multiple criteria.

this is fake example

import * as v from 'valibot';

// Define the userType variant schema
const userType = v.variant('userType',[
 v.object({
    userType: v.literal('administrator'),
    requirement: v.array(v.string())
 }),
 v.object({
    userType: v.literal('user'),
    description: v.string()
 })
]);

// Define the typeEmails variant schema
const typeEmails = v.variant('email',[
 v.object({
    email: v.literal('[email protected]'),
    expired: v.date()
 }),
 v.object({
    email: v.literal('[email protected]'),
    description: v.string()
 })
]);

// Define the typeUrl variant schema
const typeUrl = v.variant('url',[
 v.object({
    url: v.literal('https://a.com'),
    domainName: v.string()
 }),
 v.object({
    url: v.literal('https://b.com'), // Corrected the typo here
    description: v.string()
 })
]);

// Define the VariantSchema that combines multiple conditions and handles null values
const VariantSchema = v.variant(['type', 'email'], [
 v.object({
    type: v.literal('email'),
    urls: v.array(v.string([v.url()])),
    email: v.literal('[email protected]'),
    // Include typeEmails.entries for more complex validation scenarios
    // Note: This is a conceptual example, as direct usage like this might not be supported
    // Consider using custom validation logic or nested schemas for complex scenarios
 }),
 v.object({
    type: v.literal('url'),
    email: v.literal('[email protected]'),
    url: v.picklist(['https://', 'https://this.com']),
 }),
 v.object({
    type: v.literal('date'),
    email: v.literal('[email protected]'),
    ip: v.picklist(['12.2.2.2', '2.2.2.2']),
    date: v.string([v.isoDate()]),
 }),
 // Adding an object to handle null values for type
 v.object({
    type: v.literal(null), // This is crucial for handling null values
    email: v.string([v.email()]), // This field can be any string or null
    url: v.string([v.url()]), // This field can be any URL or null
 }),
]);

JolianGof avatar Feb 28 '24 18:02 JolianGof

It looks like union is the right schema for your use case:

  • https://valibot.dev/guides/unions/
  • https://valibot.dev/api/union/

fabian-hiller avatar Feb 28 '24 18:02 fabian-hiller

It is now possible to deeply nest multiple variant schemas to achieve this goal.

fabian-hiller avatar May 22 '24 00:05 fabian-hiller