colinhacks / zod

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

Zod Preprocess returning unknown type #3537

Open enchorb opened 6 months ago

enchorb commented 6 months ago

The type returned from z.preprocess is giving out type unknown

export const string_validation = z.preprocess(
  (value) => {
    if (typeof value !== 'string') return value;
    return value.trim().replace(/\s\s+/g, ' ');
  },
  z.string().min(1)
);

export const boolean_validation = z.preprocess((bool) => {
  if (typeof bool === 'boolean') return bool;
  return bool === 'true';
}, z.boolean());

export const number_validation = z.preprocess(
  (num) => (!num && num !== 0 ? num : Number(num)),
  z.number()
);

It's annoying but don't use preprocess in too many places so for now fixing by setting the type explicitly

// Before
export const string_validation: .ZodEffects<z.ZodString, string, unknown>
export const boolean_validation: z.ZodEffects<z.ZodBoolean, boolean, unknown>
export const number_validation: z.ZodEffects<z.ZodNumber, number, unknown>

// After
export const string_validation: .ZodEffects<z.ZodString, string, string>
export const boolean_validation: z.ZodEffects<z.ZodBoolean, boolean, boolean>
export const number_validation: z.ZodEffects<z.ZodNumber, number, number>
samchungy commented 5 months ago

This depends on what type util you're using to infer the type? Are you using infer, input or output?

enchorb commented 1 month ago

I use infer for my types and that works fine, types are as expected. This fails after safeParse / parse where the output for all preprocessed types is unknown

const product_validation = z.object({
   name: string_validation
   description: z.string(),
   hidden: boolean_validation.default(false),
   popular: z.boolean()
}).partial();

// Types Are As Expected
export type Product = z.infer<typeof product_validation>; 

// Types Are Unknown For All Preprocessed Types
const parsed = product_validation.safeParse({ name: 'Test Product', description: 'This is a product', hidden: false, popular: true )}.data; 
samchungy commented 1 month ago

Looks fine to me in this playground

image
andrew-pledge-io commented 1 month ago

I think I have a similar issue. For me it appears when using z.ZodType.

See this playground.

import {z} from 'zod';

const preprocessSchema = z.preprocess((val) => (val === '' ? null : val), z.coerce.date());
const noPreprocessSchema = z.coerce.date();

function doSafeParse<T>(schema: z.ZodType<T>) {
  return schema.safeParse({})
}

// unknown -- broken?
const parsed = doSafeParse(preprocessSchema).data;
// Date | undefined
const parsed2 = preprocessSchema.safeParse({}).data;
// Date | undefined
const parsed3 = doSafeParse(noPreprocessSchema).data;
// Date | undefined
const parsed4 = noPreprocessSchema.safeParse({}).data;
samchungy commented 1 month ago

I think I have a similar issue. For me it appears when using z.ZodType.

See this playground.

import {z} from 'zod';

const preprocessSchema = z.preprocess((val) => (val === '' ? null : val), z.coerce.date());
const noPreprocessSchema = z.coerce.date();

function doSafeParse<T>(schema: z.ZodType<T>) {
  return schema.safeParse({})
}

// unknown -- broken?
const parsed = doSafeParse(preprocessSchema).data;
// Date | undefined
const parsed2 = preprocessSchema.safeParse({}).data;
// Date | undefined
const parsed3 = doSafeParse(noPreprocessSchema).data;
// Date | undefined
const parsed4 = noPreprocessSchema.safeParse({}).data;

You aren't declaring your function types correctly. Please read this.