zod icon indicating copy to clipboard operation
zod copied to clipboard

Cannot read properties of undefined (reading '_parse')

Open Rocinante89 opened this issue 2 years ago • 12 comments

Hey, I've come across this issue in my project while composing schemas from imported zod objects/enums.Here is the code:

CountryCode.ts

import { z } from 'zod';

const countryCode = z.enum(['IE', 'GB']);

type CountryCode = z.infer<typeof countryCode>;

const countrySchema = z.object({
  alpha2: z.string(),
  alpha3: z.string(),
  code: countryCode,
  name: z.string(),
  region: z.string(),
  regionCode: z.number(),
  subRegion: z.string(),
  subRegionCode: z.number(),
});

type Country = z.infer<typeof countrySchema>;

export { countryCode, countrySchema };
export type { Country, CountryCode };

CurrencyCode.ts

import { z } from 'zod';

const currencyCode = z.enum(['EUR', 'GBP']);

type CurrencyCode = z.infer<typeof currencyCode>;

const currencySchema = z.object({
    code: currencyCode,
    name: z.string(),
    minorUnit: z.number(),
  });


  type Currency = z.infer<typeof currencySchema>;

export { currencyCode, currencySchema };    
export type { CurrencyCode, Currency };   

If i import the above like so: invitePerson.ts

import { z } from 'zod';
import { countryCode, currencyCode } from '../../api';

const invitePersonSchema = z.object({
  startDate: z.date().optional(),
  firstName: z.string().min(1, 'First name is required'),
  lastName: z.string().min(1, 'Last name is required'),
  role: z.string().optional(),
  dateOfBirth: z.date().optional(),
  email: z.string().email('Email is required'),
  residentialAddress: z.object({
    addressLine1: z.string().optional(),
    addressLine2: z.string().optional(),
    addressLine3: z.string().optional(),
    city: z.string().optional(),
    countryCode: countryCode,
    postalCode: z.string().optional(),
  }),
  annualSalary: z
    .string()
    .min(1, 'Annual salary is required')
    .transform(value => parseInt(value, 10)),
  salaryCurrency: currencyCode,
  taxId: z.string().optional(),
});

type InvitePersonData = z.infer<typeof invitePersonSchema>;

export { invitePersonSchema };
export type { InvitePersonData };

I get the error in the title. However, if I define the enums directly in the invitePersonSchema and don't import, there is no error. I would have expected this to be allowed, so please forgive me if I am misusing the library in some way.

Note: this was already discussed here, but the last comment provides a reproducible example which I have copied below, just in case it helps: https://github.com/colinhacks/zod/issues/643

I ran into the same error message and found this issue. Now while my problem is likely a different another one, I thought sharing this might still help others ending up here.

Essentially what I was doing is exporting a schema from one file, using it in another one to compose another schema, and then importing that composed schema back into the first file. While there isn't a cyclic dependency per se, it causes the mentioned error at runtime.

See this CodeSandbox for an example: https://codesandbox.io/s/patient-butterfly-hsg3q?file=/src/index.ts The message isn't as descriptive as in my local (larger) project, but removing this exact kind of import solved it for me. (Maybe this is generally "wrong" in terms of my tsconfig or simply unsupported by zod and I don't know it, but I would intuitively expect it to work 🙈.)

Rocinante89 avatar Jun 07 '22 14:06 Rocinante89

I seem to be running into this issue as well while migrating from jest to vitest and it seems to have to do with a cjs vs esm.

It worked fine with cjs but seems to break with vitest's esm-first approach. Not sure if this info is helpful or not, will keep looking into this.

krivachy avatar Jun 14 '22 21:06 krivachy

Hmmm, I've been using tsup to manage packages in a monorepo and my schemas package is esm.

Rocinante89 avatar Jun 15 '22 10:06 Rocinante89

I just discovered zod recently and was about to migrate our project to it, but apparently I'm running into this issue too.

The Enum is in a different file (where it belongs to) and I get this cryptic error message ("TypeError: Cannot read properties of undefined (reading '_parseSync')"). If I temporarly put the enums inline in the same file where the zod object is created, everything works.

plehnen avatar Aug 01 '22 11:08 plehnen

Same issue, receiving TypeError: Cannot read properties of undefined (reading '_parse') while having

import { FooDomain } from '../../types/foo';

foo.ts:

const FooDomain = z.string().refine(
    (arg) => isValidFooDomain(arg),
    (arg) => ({ message: `'${arg}' is not a valid foo domain` }),
);

so it does not seem to be purely related to Zod enums

njokipal avatar Aug 03 '22 10:08 njokipal

I solved it by modifying some imported files: splitting them or joining other parts together. Apparently this is some kind of circular dependency issue - even though the rest of the code did just work fine all the time. But after restructuring everything worked. (Note that I also had to seperate stuff which actually wasn't even called but webpack did combine it into a bundle and therefore caused that issue)

plehnen avatar Aug 03 '22 16:08 plehnen

The Enum is in a different file (where it belongs to) and I get this cryptic error message ("TypeError: Cannot read properties of undefined (reading '_parseSync')"). If I temporarly put the enums inline in the same file where the zod object is created, everything works.

I had a Zod schema defined in a file using schemas defined in another. I was having the about the same issue (Cannot read properties of undefined (reading '_asyncParse').

Moving the schemas in the same file fixed it.

I then got the issue again in another context. I looked at this issue which was mentioned earlier and avoiding the "circular import" mentioned here fixed it, definitely I hope.

rchampourlier avatar Aug 08 '22 08:08 rchampourlier

Had the same issue, turned out to be a circular dependency between two files

scootklein avatar Aug 11 '22 17:08 scootklein

We got the same issue today, couldn't seem to find any circular dependency or anything like that. We were calling safeParse on a zod schema within a typescript type guard. The zod schema have other schemas imported from other places, but no circularity of any kind what we could see. We are also using vitest. So esm (module type stuff) could maybe be a factor. Didn't have time to get to the bottom of it so replaced the safeParse call with a simpler check in the code for now. But it's a real issue.

emanuel-lundman avatar Sep 22 '22 21:09 emanuel-lundman

Facing the same issue and this is a clear reproducible example without any circular dependency: https://codesandbox.io/s/patient-butterfly-hsg3q?file=/src/index.ts

cromaniuc avatar Oct 05 '22 07:10 cromaniuc

this issue is not really related to zod but to the way circular imports work. when you have a circular dependency between two files, the second one to be loaded will get "undefined" for all the objects exported by the first one.

for example, if you have these two files:

// a.ts
import { b } from "./b";
export const a = {};
console.log("in a.ts:", "a", a, "b", b);

// b.ts
import { a } from "./a";
export const b = {};
console.log("in b.ts:", "a", a, "b", b);

when running a.ts, you get this output:

$ ts-node src/a.ts
in b.ts: a undefined b {}
in a.ts: a {} b {}

note that a is undefined when logging from file b.ts. this happens regardless of whether there is a circular dependency between the objects a and b themselves.

in your example, index.ts imports otherFile.ts which imports index.ts. the value Severity is undefined when creating the schema Issue, which then fails when calling parse.

ealmansi avatar Oct 06 '22 18:10 ealmansi

i'd say the standard trick for solving the issue is to just avoid the circular imports (in your case, that would be moving Severity into its own file which is imported by the other two).

or else you can defer the execution of the code that needs both files to be loaded completely by returning a factory function instead of the object itself. e.g.:

// a.ts
import { b, log } from "./b";
export const a = {};
log();
console.log("in a.ts:", "a", a, "b", b)

// b.ts
import { a } from "./a";
export const b = {};
export const log = () => console.log("in b.ts:", "a", a, "b", b);

which then gives you this output:

$ ts-node src/a.ts
in b.ts: a {} b {}
in a.ts: a {} b {}

ealmansi avatar Oct 06 '22 18:10 ealmansi

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Dec 05 '22 21:12 stale[bot]

@cromaniuc

Facing the same issue and this is a clear reproducible example without any circular dependency: https://codesandbox.io/s/patient-butterfly-hsg3q?file=/src/index.ts

// otherFile.ts
import { Severity } from "./index";

// index.ts
import { Issue as IssueImported } from "./otherFile";

That's circular dependency 😁

SomiDivian avatar Feb 09 '23 17:02 SomiDivian