colinhacks / zod

TypeScript-first schema validation with static type inference
https://zod.dev
MIT License
33.92k stars 1.18k forks source link

[BUG] Generic Function with Object Extend/Merge not treating optional properties as optional and expecting them anyways. #3665

Open MrMysterius opened 3 months ago

MrMysterius commented 3 months ago

The following is my code, where in the createParser function, where on the return it wants all properties in the schema from the ResultCreator/createResult function even though the omitted properties are optional and shouldn't be required.

The two Errors look the following in order of lines: Code_WxPNisx1AF

Code_CtU0jRz4W8

Result.ts:

import { z } from "zod";

export const VResultOptions = z.object({
  success: z.boolean(),
  message: z.string().optional(),
  errors: z.array(z.any()).optional(),
});

export type ResultOptions = z.infer<typeof VResultOptions>;

export function ResultCreator<T extends z.ZodTypeAny>(data_schema: T) {
  let merged = VResultOptions.extend({ data: data_schema.optional() });
  return function (options: z.input<typeof merged>) {
    let res = merged.safeParse(options);
    if (!res.success) throw res.error;
    return res.data;
  };
}

Parser.ts

import { ResultCreator } from "./Result";
import { fromError } from "zod-validation-error";
import { z } from "zod";

export function createParser<T extends z.ZodTypeAny>(schema: T) {
  const createResult = ResultCreator(schema);
  return function (item: z.input<typeof schema>) {
    const parsed = schema.safeParse(item);
    if (!parsed.success) return createResult({ success: false, message: `${fromError(parsed.error).toString()}` }); //EXPECTS DATA AND ERROR PROPERTY ANYWAYS
    return createResult({ success: true, message: "Parsed without problems", data: parsed.data }); //EXPECTS THE ERROR PROPERTY ANYWAYS
  };
}

I believe this is not the expected behavior, since the properties should be optional as defined and not required. Also I hope I am not a big dumb dumb, and this behavior is normal.

Side note I did try setting every property even as undefined and then it worked, so it really does want all the properties even though some are optional

MrMysterius commented 3 months ago

It might not be a problem with the extend/merge, since this does not work as well. ¯\_(ツ)_/¯

export function ResultCreator<T extends z.ZodTypeAny>(data_schema: T) {
  let merged = z.object({
    success: z.boolean(),
    message: z.string().optional(),
    errors: z.array(z.any().optional()).optional(),
    data: data_schema.optional(),
  });
  return function (options: z.input<typeof merged>) {
    let res = merged.safeParse(options);
    if (!res.success) throw res.error;
    return res.data;
  };
}