Zod offers the flexibility to customize validation for optional keys

Currently, I am exploring the utility of using zod within my application. I am facing a minor issue when attempting to parse an object that may contain optional keys. While using .passthrough allows the keys to remain in the object, I am interested in custom validating the keys or ensuring that the key names and types are valid. The .catchall method only permits specifying a type for all optional keys, but I require the ability to custom validate each optional key individually.

import {z} from 'zod';

// mandatory user information
const user = z.object({
    id: z.number(),
    name: z.string(),
});

// additional keys such as:
// string: key in the format /^add_\d{3}_s$/
// number: key in the format /^add_\d{3}_n$/ 

add_001_s: z.string()
add_002_s: z.string()
add_003_n: z.number()
add_004_n: z.number()

Answer №1

In order to tackle this problem, I propose a solution involving the combination of three schemas:

import { z } from "zod";

const mandatoryFields = z.object({
  id: z.number(),
  name: z.string()
});

const stringRegex = /^add_\d{3}_s$/;
const optionalStringFields = z.record(
  z.string().regex(stringRegex),
  z.string()
);

const numberRegex = /^add_\d{3}_n$/;
const optionalNumberFields = z.record(
  z.string().regex(numberRegex),
  z.number()
);

While these three schemas form the basis of the desired type, combining them using and is challenging due to conflicts between the record types and the inability to include mandatory fields in either record. A vanilla TypeScript type for incoming data without an extensive enumerated type would be complex to define.

To overcome this challenge, I suggest using these base schemas to preprocess the input into a new object that separates the three schema components. This preprocessing does not validate the input but extracts the fields for validation by the final schema:

const schema = z.preprocess(
  (args) => {
    const unknownRecord = z.record(z.string(), z.unknown()).safeParse(args);
    if (!unknownRecord.success) {
      return args;
    }
    const entries = Object.entries(unknownRecord.data);
    const numbers = Object.fromEntries(
      entries.filter(
        ([k, v]): [string, unknown] | null => k.match(numberRegex) && [k, v]
      )
    );
    const strings = Object.fromEntries(
      entries.filter(
        ([k, v]): [string, unknown] | null => k.match(stringRegex) && [k, v]
      )
    );
    return {
      mandatory: args,
      numbers,
      strings
    };
  },
  z.object({
    mandatory: mandatoryFields,
    numbers: optionalNumberFields,
    strings: optionalStringFields
  })
);

Using this approach, when you input:

const test = schema.parse({
  id: 11,
  name: "steve",
  add_101_s: "cat",
  add_123_n: 43,
  dont_care: "something"
});

console.log(test);
/* Outputs:
mandatory: Object
  id: 11
  name: "steve"
numbers: Object
  add_123_n: 43
strings: Object
  add_101_s: "cat"
*/

You receive separate sections for each component, excluding unnecessary fields like dont_care, which is an advantage compared to using passthrough for a similar purpose.

Overall, this method seems optimal unless you wish to develop an extensive optional mapped type for the current records, which could provide better typings but result in a considerably larger file to encompass all fields.

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 process for requesting a specific condition using jscript and jquery?

I'm experimenting with the following code: while (true) { $(document).ready(function() { setInterval(function () { if ($("h2").text() == "What is a light-year?") { $("#choice2").delay(200).queue ...

The function .val() is not a recognized method

hello everyone here is the section of my HTML code: <div class="radio"> <input type="radio" name="certain" id="oui" value="oui"/> <label for="oui"> oui </label> <br /> <input type="radio" name="certain" id=" ...

What is the process for retrieving the address of the connected wallet using web3modal?

I've been working on an application using next.js and web3. In order to link the user's wallet to the front-end, I opted for web3modal with the following code: const Home: NextPage = () => { const [signer, setSigner] = useState<JsonRpcSig ...

JavaScript code that involves manipulating dates within a nested loop

I'm currently developing a booking system where the pricing varies based on seasons that recur annually. The overall functionality is working smoothly, however, I am encountering difficulties with handling the recurring seasons. I have implemented mom ...

Issue with redirecting after confirming user info permissions on Facebook login

Can anyone assist me with the final step of my Facebook login feature? The current issue is that when a new user first signs in, they are greeted with a popup labeled 'www.facebook.com/v11.0/dialog/oauth' that asks them to authorize my page to a ...

How can I prevent event propagation in Vuetify's v-switch component?

Currently, I am working with Vuetify and have incorporated the use of v-data-table. Whenever I click on a row within this data table, a corresponding dialog box represented by v-dialog should open. Additionally, each row contains a v-switch element. Howeve ...

Display a notification to the user prior to reloading the page

I have created a code for logging attendance at work using a barcode reader. The user simply needs to scan their card with a barcode to register their attendance. let $scannerInput = $(".scanner-input"); $(document).ready(function(){ $scannerInput.focu ...

Attempting to retrieve AJAX response in JavaScript by utilizing an OOP object

Here is the code snippet I am working with: function CreateDiv(D) { D.body = function () { var d = $.ajax({ type: "GET", url: "Default.aspx", data: 'Ext ...

Navigating between different components in React Router V4 allows for seamless transitions without having to reload the

I am currently learning React with React Router V4 and I have a specific scenario in mind that I would like to achieve, possibly illustrated by the image below: Click on the "Next" button Trigger a click event to Component A ("button got clicked") Upon c ...

What is the procedure to change a matter body's isStatic property to false in matter.js upon pressing a key?

During my recent project, I encountered a challenge in trying to set the isStatic property of a Matter Body in matter.js to false when a key is pressed. if (keyIsPressed(UP_ARROW)) { this.body.isStatic(false) } Could you provide guidance on the correct ...

Utilizing .trigger repeatedly within a loop

I am in search of a solution to iterate through items in a select box, checking if any of them already have quantity data saved in the API, and then appending those items to the page. My current code achieves this by using .trigger('change'), bu ...

Is it possible to iterate through a nested object with a dynamic number of fields?

{ "pagesections": [ { "title": "Leadership Team", "sections": [ { "title": "Co-Founders/Co-Presidents", ...

The 'palette' property is not found on the Type 'Theme' within the MUI Property

Having some trouble with MUI and TypeScript. I keep encountering this error message: Property 'palette' does not exist on type 'Theme'.ts(2339) Check out the code snippet below: const StyledTextField = styled(TextField)(({ theme }) = ...

Step-by-step guide on incorporating a hyperlink within a row using @material-ui/data-grid

I am currently using @material-ui/data-grid to showcase some data on my webpage. Each row in the grid must have a link that leads to the next page when clicked. I have all the necessary data ready to be passed, however, I am facing difficulty in creating a ...

Failure of event watcher on dynamically updated content

Any help would be greatly appreciated! :) I am currently using JavaScript to dynamically add fields to a document, but I have noticed that the event listener only works on predefined fields. For instance, in the code snippet below, the 'lozfield&apo ...

How can I incorporate a feature in my Angular application that allows users to switch between different view types, such as days, using JavaScript

Greetings, community! I am currently utilizing version 5 of the fullcalendar library from https://fullcalendar.io/ in my Angular 9 application. I have noticed that the calendar offers various options to change the view type as shown below: https://i.stac ...

Establishing relationships with Sequelize between tables in different schemas

Currently, I am working on a project that involves using Sequelize and PostgreSQL as the database. In this project, I have implemented dynamic schema creation whenever a new user registers on the website. Specifically, I have a table called user_credentia ...

Can Three.js be used to create a compact canvas?

I've successfully implemented a three.js scene on my website where users can drag to rotate an object. However, I don't want the scene to take up the entire webpage. I tried adjusting the field parameters with the following code: renderer.setSiz ...

Utilizing Knockout to Load JSON Data in DevExtreme

As a newcomer to both javascript and devextreme, I am currently navigating my way through. I successfully created a webpage that loads a json file, but I'm facing challenges implementing it using devextreme. I have a dxDataGrid where I intend to disp ...

Laravel triggers a 'required' error message when all fields have been filled

As I attempt to submit a form using an axios post request in laravel, I encounter an issue with the validation of the name and age fields, along with an image file upload. Here is a breakdown of the form structure: Below is the form setup: <form actio ...