Determine data type based on key of object within a Zod array

Trying to extract the type from a key within an array of objects using Zod presents some challenges, especially when the array is nested within another object.

To illustrate the issue:

const obj = z.object({
  nestedArray: z.array(z.object({ valueIWant: z.string() }))
})

// Despite attempting to assign the type z.ZodArray(), it still remains as z.ZodObject
const arrayOfObjs = obj.pick({ nestedArray: true })

// Accessing a value in the array using z.ZodArray().element
arrayOfObjs.element.pick({ valueIWant: true })

Expected behavior for arrays in Zod:

// Should be of type z.ZodArray
const arr = z.array(z.object({ valueIWant: z.string() }))

const myValue = arr.element.pick({ valueIWant: true })

The current dilemma involves:

Working with an API that returns the following object structure:

export const wordAPI = z.object({
  words: z.array(
    z.object({
      id: z.string(),
      word: z.string(),
      translation: z.string(),
      type: z.enum(['verb', 'adjective', 'noun'])
    })
  )
})

In specifying tRPC input parameters, I aim to enable filtering by word type. However, manually rewriting

z.enum(['verb', 'adjective', 'noun'])
poses potential issues down the line. How can the word type be inferred through the array?

tRPC endpoint definition:

export const translationsRouter = createRouter().query('get', {
  input: z.object({
    limit: z.number().default(10),
    avoid: z.array(z.string()).nullish(),
    wordType: z.enum(['verb', 'adjective', 'noun']).nullish() // <-- infer here
  }),
  [...]
})

Answer №1

To efficiently handle this situation, my suggestion is to extract the wordType field as a separate schema and incorporate it within the z.object. Here's how you can do it:

const wordTypeSchema = z.enum(["verb", "adjective", "noun"]);
type WordType = z.infer<typeof wordTypeSchema>;
export const wordAPI = z.object({
  words: z.array(
    z.object({
      id: z.string(),
      word: z.string(),
      translation: z.string(),
      type: wordTypeSchema
    })
  )
});

Then, you can use the WordType type wherever necessary.

If needed, you can also fetch the type from your nested object like this:

type WordAPI = z.infer<typeof wordAPI>;
type WordType = WordAPI['words'][number]['type'];
//    ^- This will include `| null` due to `.nullable`
// To exclude | null, utilize:
type WordTypeNotNull = Exclude<WordType, null>;

I would advocate for the first method since it offers more reusability and remains adaptable in case the object structure changes.

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

Issue with Readonly modifier not functioning as expected in Angular/Typescript

My goal is to create a component property that is read-only. However, I am facing an issue where the readonly modifier does not seem to have any effect. View example on stackblitz According to the documentation, once I initialize the cars property in the ...

PHP Adding Data to a Multi-Dimensional Array

I've been working on a function that receives an array of URLs as input. The goal is to extract data from each webpage and store it in an array. Below is the code for my function: function getItems($urls) { $itemInfo = array(); foreach ($url ...

An error occured in angular2: Cannot access the 'title' property of undefined

Here is the code snippet for my custom component: export class MoviedetailComponent implements OnInit { movie:any constructor( private getmovie: GetmovieService, private router: Router, private rout: ActivatedRoute ) { } ngOnInit() { this.r ...

Is there a way to toast the values of an array retrieved from a getter within my object?

My data in the format of a JSON Array looks like this and I am attempting to display the user_review_ids values - 63,59,62 using a toast message. However, instead of getting the actual values, I keep receiving what seems to be a reference to them, such as ...

The TypeScript compilation is not able to find index.ts at the moment

When I tried to run 'ng serve', I encountered the following error message: The TypeScript compilation is missing node_modules/angular2-indexeddb/index.ts. It is crucial to ensure that this file is included in your tsconfig under the 'file ...

Tips for parsing and manipulating a json matrix array within Excel-VBA

Looking to convert JSON text into a JSON object in Excel-VBA, including a matrix/array that needs to be addressed (setting a VBA variable to the value). Previously, I successfully parsed nested/keyed JSON text using the "JsonConverter.parseJSON" method. H ...

unable to access environment file

Recently, I delved into the world of TypeScript and created a simple mailer application. However, I encountered an issue where TypeScript was unable to read a file. Placing it in the src folder did not result in it being copied to dist during build. When I ...

What is the process for validating observations with an observer confirmation?

Can you explain what the of() function creates in this scenario and how it operates? public onRemoving(tag): Observable<any> { const confirm = window.confirm('Do you really want to remove this tag?'); return Observable.of(tag).fil ...

Trouble with the array returned by preg_split in PHP

I am currently in the process of developing a PHP script to validate usernames and passwords. I am taking it step by step, starting with a password file that contains only one line: user:0011 To parse the lines in this file, I am using preg_split and hav ...

Issue with ngx-extended-pdf-viewer when using URL

I'm struggling to display my PDF file on a viewer using a URL in TypeScript. I am utilizing ngx-extended-pdf-viewer. Below is a snippet of my code with the URL replaced: <ngx-extended-pdf-viewer *ngIf="!isFirefox" [src]="'http://www.chi ...

What is the best way to decouple api and async request logic from React components while incorporating Recoil?

Currently, I find myself inserting my request/api logic directly into my components because I often need to set state based on the response from the backend. On my settings page, I have a function that saves the settings to recoil after the user clicks sa ...

What is the process for using infer to determine the return type of a void function?

I am trying to gain a better understanding of how to utilize the infer keyword in TypeScript. Is this an appropriate example demonstrating the correct usage of infer? I simply want to infer the return type of the function below: const [name, setName] = u ...

What are some ways to utilize an empty array that has been declared in React's initial state?

I am currently in the process of developing an application that displays a collection of various lists. However, I have encountered a roadblock when attempting to access an empty array that I initialized when setting up the initial state. Here is the state ...

TypeScript: Defining an Array Type within a Namespace or Module

Within a specific namespace, I have the following code: const operation1 = Symbol("operation1"); const operation2 = Symbol("operation2"); export interface Array<T> extends IConjable<T>, ISeqable<T> {} Array.prototype[op ...

I'm trying to resolve the "Uncaught TypeError: Cannot read property '0' of null" error that keeps popping up in my Mobx action within a Firebase and React application. Can anyone offer some guidance on

I've encountered some errors while working on a Mobx React application that occurs when I navigate to the /login page, despite being logged in. Here's a snippet of my code: index.tsx (Code Snippet Here) App.tsx (Code Snippet Here) Login.tsx ...

An issue has occurred: Uncaught (in promise): NullInjectorError: R3InjectorError(AppModule)[NavbarComponent -> NavbarComponent

I've been working on implementing Google Auth login with Firebase, but I keep encountering an issue when trying to load another component or page after logging in. I've spent the entire day trying to debug this problem and it's really frustr ...

A guide on calculating the total values for each key in a multidimensional array

If we take an array with the following structure: $data = array( 'blue' => array( '1' => array(4), '2' => array(8, 3) ), 'yellow' => array( '2' => array(3 ...

Why is the imported package not being recognized in the namespace declaration of my Node.js TypeScript file?

Currently, I am utilizing the WebStorm IDE developed by JetBrains to modify a TypeScript file within a Node.js v8.6.0 project. The JavaScript version set for this project is JSX Harmony. In the beginning of the TypeScript source file, there is an import st ...

Showing error messages in Angular when a form is submitted and found to be invalid

My form currently displays an error message under each field if left empty or invalid. However, I want to customize the behavior of the submit button when the form is invalid. <form #projectForm="ngForm" (ngSubmit)="onSubmit()"> ...

Does the term 'alias' hold a special significance in programming?

Utilizing Angular 2 and Typescript, I have a component with a property defined as follows: alias: string; Attempting to bind this property to an input tag in my template like so: <input class="form-control" type="text" required ...