I have a couple schemas, where one uses an array of the other as a value. In 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>;
But when I want to access a Person, I want the actions property to be a single object. The conversion looks 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'},
];
Become something like :
{
run: {action: 'run', id: '1234', description: 'run up'},
kick: {action: 'kick', id: '5334', description: 'kick it'},
...
}
So, I changed my PersonSchema to reflect it like this,
const PersonSchema = {
id: z.string(),
actions: ActionSchema.array().optional().transform((actions) => {
return actions
? Object.fromEntries(actions.map((action) => [action.name, action]))
: undefined;
}), }
This works as intended when I output the Person.
The problem I’m having is I am getting an error in typescript when creating a new Person like this
const newPerson:Person = {
id: 'PER1234',
actions: actionsArray }
This is my error:
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 know the properties are mismatched because of the transform I did on actions.
But I want the schema to accept a Actions array as input when creating a new Person, then change it to the format I want after parsing, when I choose to print or access the Person.
Sorry if it is a little confusing. Any help would be great.
I tried a lot of things to fix the error. The code “works” but typescript doesn’t approve of it.
I can create the new Person using the actions array and typing it as any like this,
const newPerson:Person = {
id: 'PER1234',
actions: actionsArray as any
}
but I would like to avoid using as any if possible.
You have two schemas. One for parsing a Person, and another for the rest of your app.
function mapActions( actions: z.infer<typeof ActionSchema>) {
return actions
? Object.fromEntries(actions.map((action) => [action.name, action]))
: undefined;
}
const ParsePersonSchema = { id: z.string(), actions: ActionSchema.array().optional() }
type ParsePerson = z.infer<typeof ParsePersonSchema>;
const PersonSchema = { id: z.string(), actions: ActionSchema.array().optional().transform(mapActions) };
type Person : z.infer<typeof PersonSchema>;
function parsedPerson(person: ParsePerson): Person {
return { ...person, actions: mapActions(person.actions) };
}
1