Skip to content

zod .defaultValue failing even with default values provided in schema #564

Description

@jv-np
function.js:64 Uncaught Error: Exhaustive match failed
    at Object.exhaustive (function.js:64:9)
    at default-value.js:21:19
    at loop (function.js:48:14)
    at Array.<anonymous> (functor.js:476:61)
    at Object.transform (function.js:78:22)
    at default-value.js:69:27
    at loop (function.js:48:14)
    at functor.js:439:20
    at functor.js:703:30
    at loop (function.js:48:35)

Reproduction

Custom type ->

import { z } from 'zod';

export class Time {
  value: number;

  get hours() {
    return Math.trunc(this.value);
  }
  get minutes() {
    return Math.trunc((this.value - this.hours) * 60);
  }

  withHours(hours: number) {
    return Time.from(hours, this.minutes);
  }

  withMinutes(minutes: number) {
    return Time.from(this.hours, minutes);
  }

  constructor(value: number) {
    this.value = value;
  }

  static from(hours: number, minutes: number) {
    return new this(hours + (minutes / 60));
  }

  private static intFormat: Intl.NumberFormatOptions = {
    minimumIntegerDigits: 2,
  };

  toString(): string {
    return `${this.hours.toLocaleString(undefined, Time.intFormat)}:${
      this.minutes.toLocaleString(undefined, Time.intFormat)
    }`;
  }

  toJSON() {
    return this.value;
  }

  static readonly zero = new Time(0);

  static forceParse(raw: string): Time {
    const parts = raw.split(':');
    if (parts.length !== 2) {
      throw new Error('Invalid time');
    }

    const hours = parseInt(parts[0]);
    const minutes = parseInt(parts[1]);
    if (isNaN(hours) || isNaN(minutes)) {
      throw new Error('Invalid time');
    }

    return this.from(hours, minutes);
  }

  static is(dat: any): dat is Time {
    return dat instanceof Time;
  }
}

export function zodTime(def?: Time) {
  if (def) {
    return z.union([
      z.instanceof(Time),
      z.number().transform(n => new Time(n)),
      z.string().transform(s => Time.forceParse(s)),
    ]).or(z.instanceof(Time).default(def));
  }
  return z.union([
    z.instanceof(Time),
    z.number().transform(n => new Time(n)),
    z.string().transform(s => Time.forceParse(s)),
  ]);
}

(It also happens if you put .default in every single variant of the union in the def case.)
Simple Code ->

const exampleSchema = z.object({
  name: zodTime(Time.zero),
});
console.log('constructed', zx.defaultValue(exampleSchema));

(also happens with name: zodTime(Time.zero).default(Time.zero))

Expected Behavior

Well, it should simply work if you already provide a default value for the property using zod.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions