Can the discriminator be preprocessed in a zod discriminated union?

Is it possible to convert string formatted numbers to numbers before using a value in z.discriminatedUnion? Here is an example code snippet:

import { z } from "zod";

const BrushColorEnum = z.enum(
  ["BLUE_SILVER", "GREEN_SILVER", "RED", "GREEN_BLACK"],
  { message: "Invalid option" }
);

const BrushTypeEnum = z.enum(["THREAD", "MIXED", "CARLITE"], {
  message: "Invalid option",
});

const noBrushSchema = z.object({
  brush_qty: z.preprocess(
    (val) => (typeof val === "string" ? parseInt(val, 10) : val),
    z.literal(0)
  ),
  brush_type: z
    .union([
      z.string().length(0, { message: "Invalid option" }),
      z.undefined(),
      z.literal(false),
    ])
    .transform(() => undefined),
  brush_color: z
    .union([
      z.string().length(0, { message: "Invalid option" }),
      z.undefined(),
      z.literal(false),
    ])
    .transform(() => undefined),
});

const brushSchema = z.discriminatedUnion("brush_qty", [
  noBrushSchema,
  z.object({
    brush_qty: z.literal("2"),
    brush_type: BrushTypeEnum,
    brush_color: BrushColorEnum,
  }),
  z.object({
    brush_qty: z.literal("3"),
    brush_type: BrushTypeEnum,
    brush_color: BrushColorEnum,
  }),
]);

console.log(brushSchema.safeParse({ brush_qty: "0" }).error); // message: "Invalid discriminator value. Expected 0 | '2' | '3'"
console.log(brushSchema.safeParse({ brush_qty: 0 })); // success

Regarding the brush_qty field, while I expected it to convert the string to a number and accept the second safeParse, it appears that the validation occurs before reaching the schema section.

In cases where converting to number is not feasible, what other options do I have besides discriminatedUnion to achieve similar functionality?

Thank you in advance!

Answer №1

Make sure to validate the relevant property using z.coerce.string() within a z.object() with only that property defined first in brushSchema. The use of passthrough explicitly allows other properties, although it is likely the default behavior -- just clarifying for transparency.

import { z } from 'zod';

const BrushColorEnum = z.enum(
  ['BLUE_SILVER', 'GREEN_SILVER', 'RED', 'GREEN_BLACK'],
  { message: 'Invalid option' }
);

const BrushTypeEnum = z.enum(['THREAD', 'MIXED', 'CARLITE'], {
  message: 'Invalid option',
});

const noBrushSchema = z.object({
  brush_qty: z.literal('0'),
  brush_type: z
    .union([
      z.string().length(0, { message: 'Invalid option' }),
      z.undefined(),
      z.literal(false),
    ])
    .transform(() => undefined),
  brush_color: z
    .union([
      z.string().length(0, { message: 'Invalid option' }),
      z.undefined(),
      z.literal(false),
    ])
    .transform(() => undefined),
});

const brushSchema = z
  .object({ brush_qty: z.coerce.string() })
  .passthrough()
  .pipe(
    z.discriminatedUnion('brush_qty', [
      noBrushSchema,
      z.object({
        brush_qty: z.literal('2'),
        brush_type: BrushTypeEnum,
        brush_color: BrushColorEnum,
      }),
      z.object({
        brush_qty: z.literal('3'),
        brush_type: BrushTypeEnum,
        brush_color: BrushColorEnum,
      }),
    ])
  );

console.log(
  brushSchema.safeParse({
    brush_qty: '2',
    brush_type: 'THREAD',
    brush_color: 'RED',
  })
);  // success

console.log(brushSchema.safeParse({ brush_qty: 0 })); // success

To handle floating points like converting "2.2" to match 2, you can employ a similar technique without additional manipulation:

const brushSchema = z
  .object({
    brush_qty: z.coerce.string().transform((arg) => parseInt(arg).toString()),
  })
  .passthrough()
  .pipe(
    z.discriminatedUnion('brush_qty', [

 // ... etc

This approach converts everything to a string initially; Any floating point values are normalized by parsing them and returning as strings through a transformation process.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the reason that when the allowfullscreen attribute of an iframe is set, it doesn't appear to be retained?

Within my code, I am configuring the allowfullscreen attribute for an iframe enclosed in SkyLight, which is a npm module designed for modal views in react.js <SkyLight dialogStyles={myBigGreenDialog} hideOnOverlayClicked ref="simpleDialog"> <if ...

the issue of undefined values continue to persist

I've been struggling with this code for a couple of days and it's causing issues. All the other functions are working fine except for this EQUAL function. Here is the code: equal.addEventListener("click", function(e){ if (screen.value == ...

Exploring Javascript parameters with the power of jquery

Is it possible to pass a parameter from PHP into a JavaScript function within HTML? I am currently facing an issue where my code crashes when it reaches a certain condition. This is the code snippet causing the problem: $str="<input type='submit&a ...

Utilizing Azure Function Model v4: Establishing a Connection with Express.js

Using Model v4 in my Azure function index.ts import { app } from "@azure/functions"; import azureFunctionHandler from "azure-aws-serverless-express"; import expressApp from "../../app"; app.http("httpTrigger1", { methods: ["GET"], route: "api/{*segme ...

Issue with Ajax and Laravel 500 (Server-side Quandary)

I'm having trouble with using AJAX in Chrome. Below is the code snippet from my web.php file: Route::view('menu','home.menu',['categories'=> App\Categories::orderBy('name')->get()->take(11),'a ...

Expansive full screen image proportions

Is there a way to showcase an image on the entire screen without distorting it, while maintaining its aspect ratio in either HTML or Javascript? The desired outcome is for the image to be centered and: - Have a height of 100% and a width of x% (where "x" ...

Trigger a JavaScript/jQuery event using code instead of user interaction

Exploring the source code of a website has sparked my curiosity about how to programmatically activate the ajax autocomplete feature on a specific text box. Here is the snippet of relevant code that I have been examining: html: <div class="input-text ...

What causes the scope to shift when incorporating a Lazy function within the module pattern in JavaScript?

Implementation 1: Working code in normal var foo1 = function() { var t1 = new Date(); console.log("initialize - one time only " + this); foo1 = function() { console.log("Executes every time after initializing (initialize should not e ...

Utilizing the backtick operator for string interpolation allows for dynamic value insertion

Greetings! Currently, I am delving into the world of Angular 2 and have stumbled upon some interesting discoveries. To enhance my knowledge of TypeScript, I decided to utilize the ScratchJS extension on the Chrome browser. During this exploration, I experi ...

Is there a way for me to programmatically modify a .env file using an npm script?

Currently, I'm managing a project with a .env file that contains confidential information. One of the key elements in this file is 'STATUS'. Just to clarify, this pertains to a Discord bot, The value assigned to the 'STATUS' var ...

An array in Javascript that contains sub-arrays within its keys

I am encountering an issue with my array structure. The main array contains sub-arrays and uses custom keys as identifiers. However, I am unable to access the array using these keys. For example, when I try to get the length of the array, it returns 0 even ...

Sorting through JSON information using JQUERY

Struggling with filtering data using jQuery and JSON. I need to exclude objects with the ID "234" or the sender named "Alan Ford" in order to create separate messaging systems for Inbox and Outbox. Directly manipulating the JSON is not an option. Here&apo ...

Using rxjs for exponential backoff strategy

Exploring the Angular 7 documentation, I came across a practical example showcasing the usage of rxjs Observables to implement an exponential backoff strategy for an AJAX request: import { pipe, range, timer, zip } from 'rxjs'; import { ajax } f ...

Issue encountered in Cypress while attempting to locate an identifier beginning with a numeric value

My struggle is with finding an element by id using Cypress. The Cypress selector playground provided the following code: get("#\33 -2") Unfortunately, when I execute this code in Cypress, it results in an error: Syntax error, unrecognized expressio ...

Submitting option values in AngularJS: A step-by-step guide

Why does AngularJS ng-options use label for value instead of just the value itself? Here is my current code: <select ng-model="gameDay" ng-options="gameDay for gameDay in gameDayOptions"> This currently displays: <select ng-model="gameDay" ng- ...

Invoke a custom AWS CodeBuild project in CodePipeline using a variety of parameters

Imagine having a CodePipeline with 2 stages structured like this: new codepipeline.Pipeline(this, name + "Pipeline", { pipelineName: this.projectName + "-" + name, crossAccountKeys: false, stages: [{ stageName: &apos ...

Issue with sending props to TypeScript React component

Having a challenge with styling a simple button component using styled components. When trying to send a prop to the component, TypeScript throws an error saying "No overload matches this call". App.tsx import React from 'react'; import Button ...

An error was encountered: Unexpected token 'var'

Upon loading my website, I encountered the following error: Uncaught SyntaxError: Unexpected token 'var' This issue seems to be related to Elementor and possibly the admin-ajax.php file. https://i.sstatic.net/OM2al.jpg If anyone has insight o ...

Why is it that masonry is typically arranged in a single column with overlapping

I have been using the appended function in my code to add elements to a masonry instance that has already been initialized. However, I am facing an issue where all the tiles are being laid out in a single column and some of them are overlapping each oth ...

Check the connectivity of Angular 2 application

I currently have an Angular 2 application running on one server, and a Java application on another server. My goal is to be able to ping the Angular application from the Java application in order to check its status (whether it is up or down). Would it b ...