Zod record with required keys
Asked Answered
H

2

3

I'm trying to create a dictionary like zod schema for an object that has many keys, defined elsewhere, and they all have the same value type. And all keys are required.

const KeysSchema = z.enum(['a', 'b', 'c', /*...*/]) // long key list
const ObjSchema = z.record(KeysSchema, z.number())

But the yielded type is:

{
    a?: number | undefined;
    b?: number | undefined;
    c?: number | undefined;
}

But what I want is:

{
    a: number;
    b: number;
    c: number;
}

I want: Record<'a', 'b', 'c', number>

but it's giving me: Partial<Record<"a" | "b" | "c", number>>

And I can't call z.record(...).required() because that's not a thing for zod records.

How can I make a record type with required keys?

See Typescript playground

Hooded answered 7/2, 2024 at 23:21 Comment(0)
H
9

Managed to solve it with a .refine()

  .refine((obj): obj is Required<typeof obj> =>
    KeysSchema.options.every((key) => obj[key] != null),
  )

It checks every key in the KeysSchema and asserts that all keys are present, and it's not a partial at all.

See Playground

Hooded answered 8/2, 2024 at 0:57 Comment(1)
Thank you! Surprisingly that gave me a Required<Partial<Record<...>>>, so I tweaked it a little bit: .refine((obj): obj is typeof obj extends Partial<infer R> ? R : never => KeysSchema.options.every((key) => obj[key] != null))Pariah
T
0

Based on Alex Wayne’s answer, I created a universal function for usage:

function createRequiredObjSchema<
  K extends string,
  V extends z.ZodTypeAny
>(
  keysSchema: z.ZodEnum<[K, ...K[]]>,
  valueSchema: V
) {
  return z.record(keysSchema, valueSchema).refine((obj): obj is Record<K, z.infer<V>> =>
    keysSchema.options.every((key) => obj[key] != null),
  );
}

Small example how it is works:

const requiredRecord = createRequiredObjSchema(KeysSchema, z.string())

type RequiredRecordExample = z.infer<typeof requiredRecord>;
/*
type RequiredRecordExample = {
    a: string;
    b: string;
    c: string;
}
*/

See Playground

Turkey answered 13/6, 2024 at 16:6 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.