Zod Schema consolidates an array of objects into a single, comprehensive object

I have been working with a couple of schemas, where one contains an array of the other as a value. For example,

const ActionSchema = {id: z.string(), name: z.string(), description: z.string()} 
const PersonSchema = { id: z.string(), actions: ActionSchema.array().optional() }
type Person = z.infer<typeof PersonSchema>;

However, when I access a Person, I prefer the actions property to be in the form of a single object. The transformation would look like this.

const actionsArray = [
  {action: 'run', id: '1234', description: 'run up'}, 
  {action: 'kick', id: '5334', description: 'kick it'}, 
  {action: 'punch', id: '2236', description: 'punch it'}, 
  {action: 'drop', id: '3434', description: 'drop it'}, 
];  

Resulting in:


{
 run: {action: 'run', id: '1234', description: 'run up'}, 
 kick:   {action: 'kick', id: '5334', description: 'kick it'}, 
 ... 
}

To achieve this, I made adjustments to my PersonSchema as follows,

const PersonSchema = { 
 id: z.string(), 
 actions: ActionSchema.array().optional().transform((actions) => {
        return actions
          ? Object.fromEntries(actions.map((action) => [action.name, action]))
          : undefined;
      }), }

This modification works perfectly when outputting the Person object.

The issue arises when trying to create a new Person using typescript like this,

const newPerson:Person = {
id: 'PER1234',
actions: actionsArray }

An error occurs stating: Index signature for type 'string' is missing in type '{id: z.string(), name: z.string(), description: z.string()} ' The expected type comes from property 'actions' which is declared here on type '{ id: z.string(), actions: ActionSchema.array().optional() }

I understand that the properties are not aligned due to the transform function applied on actions. My goal is for the schema to accept an Actions array as input during the creation of a new Person, and then modify it to the desired format after processing, when accessing or printing the Person.

I apologize if this explanation is unclear. Any assistance would be greatly appreciated.

I have attempted various solutions to resolve the error. While the code "works", typescript does not validate it. I can create the new Person using the actions array and specifying it as any, like this,

const newPerson:Person = {
id: 'PER1234',
actions: actionsArray as any
}

However, I am hoping to avoid using 'as any' if possible.

Answer №1

You are working with two distinct schemas in your project. One schema is dedicated to parsing a Person object, while the other schema handles data processing for the rest of your application.

function processActions(actions: z.infer<typeof ActionSchema>) {
    return actions
          ? Object.fromEntries(actions.map((action) => [action.name, action]))
          : undefined;
}

const ParsePersonDataSchema = { id: z.string(), actions: ActionSchema.array().optional() }
type ParsePersonData = z.infer<typeof ParsePersonDataSchema>;

const PersonDataSchema = { id: z.string(), actions: ActionSchema.array().optional().transform(processActions) };
type PersonData : z.infer<typeof PersonDataSchema>;

function transformPersonData(person: ParsePersonData): PersonData {
    return { ...person, actions: processActions(person.actions) };
}

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 best way to use an HTML dropdown menu to select a field with an object as its data type

Objective: Establish a booking instance using the Angular frontend Approach: I am configuring the booking field "category" with an HTML option input through TypeScript. The field is of object data type, consisting of two fields - id and name. Issue: Whil ...

Removing Multiple Object Properties in React: A Step-by-Step Guide

Is there a way in React to remove multiple object properties with just one line of code? I am familiar with the delete command: delete obj.property, but I have multiple object properties that need to be deleted and it would be more convenient to do this i ...

The declaration '() => string' cannot be assigned to type 'string' in a Typescript React program

There have been many questions similar to mine, but none of the answers seem to solve my issue. The closest answer I found was here, however, it also doesn't work for me. I have a Tsx code snippet like this: <img src={getLogo} alt="Airline Lo ...

What steps can I take to fix error TS7015 in my TypeScript, Next.js, and React application when I encounter an index expression that is not a number data type?

I encountered an error stating "Element implicitly has an 'any' type because the index expression is not of type 'number'.ts(7015)" The file I imported is import { useAmazon } from "@context/amazon"; I used it as follows con ...

Creating a refined parameter type in Typescript using a discriminator

Looking to work with a legacy API that has the following structure (playground link)... type Command1 = { cmd: "my first command", arg1: string, arg2: boolean } type Command2 = { cmd: "my second command", foo: strin ...

Exploring the Usage of Jasmine Testing for Subscribing to Observable Service in Angular's OnInit

Currently, I am facing challenges testing a component that contains a subscription within the ngOnInit method. While everything runs smoothly in the actual application environment, testing fails because the subscription object is not accessible. I have att ...

Using TypeScript to create a generic type that wraps around HTMLElements

I attempted to execute this action, however the assignment to this.element is not working and I am unsure why. class Elem<T> { public element : T; constructor(typeElement:string){ this.element = document.createElement(typeElement); ...

Cleaning up HTML strings in Angular may strip off attribute formatting

I've been experimenting and creating a function to dynamically generate form fields. Initially, the Angular sanitizer was removing <input> tags, so I discovered a way to work around this by bypassing the sanitation process for the HTML code stri ...

The Route.ts file does not export any HTTP methods

Encountering an error while trying to migrate code from Next JS 12 with pages router in Javascript to Next JS 13 with TypeScript. ⨯ Detected default export in 'vibe\src\app\api\auth[...nextauth]\route.ts'. Export a name ...

What is a more effective approach for managing form data with React's useState hook?

Seeking a more efficient solution to eliminate redundancy in my code. Currently, I am utilizing useState() for managing user data, which results in repetition due to numerous fields. Below is a snippet of my current code: const [lname, setLast] = useState& ...

Prevent navigation if the URL is empty

I am dealing with a calendar class that has a URL property which sometimes may be null. I want to prevent users from navigating if the URL is null (as it currently navigates to the root URL). This is what I have: <a href="{{calendar.url}}" target="_bl ...

Experiencing a platform browser error since updating to Ionic 5 from Ionic 4

ERROR: Uh-oh! Looks like there's an issue in the node_modules directory. The error message reads: ERROR in node_modules/@angular/platform-browser-dynamic/platform-browser-dynamic.d.ts(15,10): error TS2305: Module '"node_modules/@angular/compiler ...

What is the method for obtaining the number of weeks since the epoch? Is it possible to

Currently, I am setting up a DynamoDb store for weekly reporting. My idea is to use the week number since 1970 as a unique identifier for each report record, similar to epoch milliseconds. Here are some questions I have: How can I determine the current w ...

TypeScript encountered an error: The get call is missing 0 type arguments

I encountered a typescript error stating "Expected 0 type arguments, but got 1" in the line where my get call is returning. Can you help me identify what is wrong with my get call in this code snippet? public get(params: SummaryParams): Observable&l ...

error TS2339: The attribute 'properties' is not accessible on the class 'TestPage'

Utilizing a typescript abstract class as the base class of my layout component in React has been essential for my project. The implementation of this base class looks something like this: import { IPageLayoutActions, IPageLayoutLocalState, IPageLayoutProp ...

Exploring the details of an object in Ionic 4: A guide to checking elements

I am currently utilizing Ionic 4 for my project. After posting data to my API, I receive the response in the following format: { "responses": [ { "Detection": { "Images": [ {"url":"https:URL"}, ...

Adding information to a table using React

I have a collection of nested data that I would like to display in a table using React. Each method is represented in one row, as illustrated in the image. How can I create a row for each method along with its corresponding page names? const methods = [{ ...

Determine the classification of the API namespace object's method type

There are two different API namespaces known as user and project. I am attempting to develop a versatile function that can determine the method type of the respective API namespace object. const user = { async getUser(id: string): Promise<string> { ...

Switch up the data format of a JSON file that is brought into TypeScript

When bringing in a JSON file into a TypeScript project with the resolveJsonModule option activated, the TypeScript compiler can automatically determine the type of the imported JSON file. However, I find this type to be too specific and I would like to rep ...

Is it possible to utilize multiple useMutation hooks within a single component?

I'm curious about how to utilize multiple mutations in a component effectively. For instance, if I need to both create and update the same component, how can I achieve this? Here's an example: const [createUser, {data}] = useMutation(CREATE_US ...