What is the best way to modify the custom error array for Zod?

When using Zod for validating react-hook-form fields, I constantly encounter the default error message "Expected array, received null". Is there a way to customize this error message in a more user-friendly manner? Below is an example of how to set a custom message for this specific scenario within the promos field, along with the corresponding output when submitting the form without making any selections in the "checkbox" array field.

Code:

    promotion: z.object({
        id: z.string().optional(),
        name: z.string({
                required_error: "A name is required.",
                invalid_type_error: "A name is required.",
            }).min(1, "A name is required.").max(150, "The name must be less than 150 characters."),
        email: z.string({
                required_error: "An email is required.",
                invalid_type_error: "This is an invalid email.",
            }).email().min(1, "An email is required."),
        promos: z.array(z.string({
                required_error: "A promotion selection is required.",
                invalid_type_error: "This is an invalid promotion selection",
            }).min(1, "A promotion selection is required.")).min(1, "At least one promotion selection is required."),
    }),

Output:

https://i.sstatic.net/yIb9T.png

Answer №1

To efficiently manage specific types of issues, you can establish a custom ZodErrorMap globally. The global scope ensures widespread applicability.

import { z } from "zod";

const customErrorMap: z.ZodErrorMap = (issue, ctx) => {
  if (issue.code === z.ZodIssueCode.invalid_type) {
    if (issue.expected === "string") {
      return { message: "bad type!" };
    }
  }
  if (issue.code === z.ZodIssueCode.custom) {
    return { message: `less-than-${(issue.params || {}).minimum}` };
  }
  return { message: ctx.defaultError };
};

z.setErrorMap(customErrorMap);

https://github.com/colinhacks/zod/blob/master/ERROR_HANDLING.md#customizing-errors-with-zoderrormap

If you prefer more specific functionality, consider applying the customized ZodErrorMap to the definition of your schema or within the schema parsing callback. However, not all schemas might support the customized ZodErrorMap parameter for application.

UPDATED 2023-01-25

For a tailored approach, try the following example:

z.setErrorMap((issue, ctx) => {
  if (issue.code === "invalid_type" && issue.expected === "array")
    return { message: "Your array is not well defined" };
  return { message: ctx.defaultError };
});

Answer №2

I couldn't find a way to handle arrays in zod, even though it's possible for other data types.

There are two workarounds available:

(1) Set a Default Value

Make sure that promos always defaults to an empty array.

.parse({...promotion, promos: promotion.promos ?? [] });

(2) Customize Validation Errors Manually

export const validateInput = <T>(
  input: Partial<T>,
  schema: z.Schema,
): Result<T, CustomError> => {
  const result = schema.safeParse(input); // instead of throwing errors

  if (result.success) {
    return { data: result.data };
  }

  return {
    ...result,
    error: {
      issues: result.error.issues.map((error) => ({
        ...error,
        message: error.code === 'invalid_type' && error.expected === 'array' && error.received === 'null' ? "your message here" error.message
      }))
    }
  }; // or throw this result
};

EDIT: Instead of calling schema.parse(input) (which throws errors) or schema.safeParse(input) (which returns errors), call the custom function provided above and handle the errors as needed.

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

Invoke method from service on click in Angular 2

I'm facing an issue with a button component that should trigger a function on click event: <button pButton type="button" label="Add EchoBeacon" (click)="insertPoint()"> constructor(private mappaService: MappaService) {} ... insertPoint() { ...

Executing Multiple Angular2 Animations Concurrently: A Guide

I am facing a challenge with a dynamic container that expands and shrinks. Inside this container, there is an element that should fade in when the container expands and fade out when it shrinks. The Issue I'm Facing When the container expands, both ...

Sometimes *ngIf doesn't function as expected

File: PageNotFoundComponent.ts import { FormsModule, NgModel } from '@angular/forms'; import { ChangeDetectorRef, Component, ElementRef, OnInit, Renderer2, ViewChild, ViewEncapsulation } from '@angular/core'; import {CommonModule} from ...

Is there a way for me to transfer a variable to a URL?

I'm attempting to send an email that includes a link with a value obtained from Firebase. While I can successfully retrieve the value, I am unsure how to add it to the existing link. Here is my code snippet: sendinvite() { var user = firebase.a ...

Angular build process encountering environment issue

When attempting to construct an Angular project, I am encountering the following problem: The file '/angular/src/environments/environment.ts' is not recognized as a module I am importing the file in this manner : import { environment } from ...

Exploring the ideal workspace for maximizing the potential of Ionic framework development

For my iOS and Android app built with Ionic and Angular, I typically use ionic serve in Chrome for easy and fast development. However, when it comes to utilizing the native HTTP API for essential tasks like communicating with a REST server, I've foun ...

Learn how to securely download files from an Azure Storage Container using Reactjs

I'm currently working on applications using reactjs/typescript. My goal is to download files from azure storage v2, following a specific path. The path includes the container named 'enrichment' and several nested folders. My objective is to ...

The reason why Type zzz cannot be assigned to type (zzz & NgIterable<xxx>) | undefined | null

Why am I receiving the message in Angular 9 that says: Type Items is not assignable to type (Items & NgIterable) | undefined | null? Despite the fact that the model is correct and there are no errors in the data, I still encounter this TypeScript warn ...

Exploring nested arrays within a JSON response using Typescript in the ReactJS environment

I have come across similar questions, but I am struggling to find a solution. My code is slightly different, and my knowledge of Typescript is limited as I only started learning it a few months ago. I have created a backend with an exposed API. When I cal ...

Ensuring accurate date formatting of API responses in TypeScript

My REST API returns data in the format shown below:- {"id": 1, "name": "New event", "date": "2020-11-14T18:02:00"} In my React frontend app, I have an interface like this:- export interface MyEvent { id ...

How to Dynamically Determine if a Value Matches a Union Type in Typescript

After creating a Union type of supported methods, the goal is to verify if a specific method belongs to the set of supported methods and then execute it dynamically. One commonly used approach is to use an array containing the names of the supported method ...

The 'innerText' property is not present in the 'Element' type. (2339)

Recently, I've been working with javaScript and I encountered some issues while writing a function. Strangely, I kept receiving error messages that I couldn't quite understand. Initially, there was a problem where every time I tried to create a j ...

What is the reason behind TypeScript permitting the assignment of a class instance to a variable that is of a different type?

When examining the given code, it raises a question about why the TypeScript compiler permits the assignment const c1: I = new C();. The issue arises when the function call c1.z(args); results in an error due to the absence of the first property in the a ...

How come I am not finding any results when trying to search for a specific subdocument within a document? (mongodb and mongoose)

I'm having trouble fetching all documents that contain the user in their array of member subdocuments. I've tried using the aggregate framework, but it always returns 0 results. Interestingly, when I use the .find() method, it works perfectly fi ...

Guide on inserting a String variable into a String within the cy.get() method

While working with Cypress, Typescript, and es6, I encountered a challenge: https://i.sstatic.net/khDFD.png I attempted to define a const within a test: const folderName = "Test" My aim was to insert the folderName into the String inside cy.ge ...

Navigating through React Native with TypeScript can be made easier by using the proper method to pass parameters to the NavigationDialog function

How can I effectively pass the parameters to the NavigationDialog function for flexible usage? I attempted to pass the parameters in my code, but it seems like there might be an issue with the isVisible parameter. import React, { useState } from 'rea ...

I am looking for the most optimal method to export a TypeScript class from my package in a way that allows it to be extended by other packages

In the process of developing a TypeScript library package called 'MyPkg,' I have created some classes (such as 'MyPkgClass') located in a specific sub-directory [e.g., src/path/to/mypkg] with essential functionality. The goal is for an ...

What could be causing my matDialog to display incorrectly in Angular version 15?

After I upgraded my project to version 15 of Angular, following the official Angular documentation, I encountered an issue with my MatDialog not opening correctly. The problem seemed to stem from removing the entryComponents and transforming all components ...

The data structure '{ one: string; two: string; three: string; }' cannot be directly assigned to a 'ReactNode'

Here is the Array of Items I need to utilize const prices = [ { name: "Standard", price: "149EGP", features: [ { one: "Add 2500 Orders Monthly", two: "Add Unlimited Products And Categories", three: "Add 20 other ...

How to Create a Flexible Angular Array Input Component

I have been working on developing reusable form input components using Angular's reactive forms. However, I am currently encountering challenges with my FormArray input component. To overcome some issues, I had to resort to using double-type casting ...