Search code examples
javascripttypescriptes6-modulescircular-dependencyvitest

zod TypeError: Cannot read properties of undefined (reading 'extend')


I have a Vite library using Vitest and Zod. Given the following folder structure

.
├── src
│   ├── leading
│   │   ├── index.ts ( 2 )
│   │   └── leadingDataSourceConfigurationSchema.ts
│   ├── index.ts ( 1 )
│   └── dataSourceConfigurationSchema.ts
└── test
    └── my.spec.ts

I created a base schema for data sources

import { z } from 'zod';

const dataSourceConfigurationSchema = z.object({
    id: z.string().min(1)
}).strict();

export { dataSourceConfigurationSchema };

exported by the index.ts ( 1 )

export * from "./leading";
export { dataSourceConfigurationSchema } from "./dataSourceConfigurationSchema";

I also have a schema extending the one above

import { z } from 'zod';
import { dataSourceConfigurationSchema } from '..';

const leadingDataSourceConfigurationSchema = dataSourceConfigurationSchema.extend({
    entityTypeId: z.string().min(1),
}).strict();

export { leadingDataSourceConfigurationSchema };

the index.ts ( 2 ) only exports this schema

export { leadingDataSourceConfigurationSchema } from "./leadingDataSourceConfigurationSchema";

When creating a test file like so

import { describe, it, expect } from 'vitest';
import { ZodError } from 'zod';
import { leadingDataSourceConfigurationSchema } from '../src';

describe('test', () => {
  it('fails.', () => {
    expect(() => leadingDataSourceConfigurationSchema.parse({})).toThrow(ZodError);
  });
});

the test fails with the following error message

TypeError: Cannot read properties of undefined (reading 'extend')

This problem might be related to

When replacing the import

import { dataSourceConfigurationSchema } from '..';

with

import { dataSourceConfigurationSchema } from '../dataSourceConfigurationSchema';

the test passes. So it seems the project is struggling with circular dependencies?

Do sub directories have to import files directly or can I fix this problem with a different approach?


Solution

  • Yes, you have a circular dependency, and due to the import order dataSourceConfigurationSchema will not yet be initialised when leadingDataSourceConfigurationSchema is intialised. This is not an issue of zod or vite (although with a proper ES6 module implementation, you'd get a TDZ error instead of a reference error from accessing a method on undefined).

    As you saw, you can fix this by importing dataSourceConfigurationSchema directly from ../dataSourceConfigurationSchema.ts and not from ../index.ts, the module that imports leadingDataSourceConfigurationSchema.ts itself.

    Another solution would be to change the order of imports in src/index.ts to

    export { dataSourceConfigurationSchema } from "./dataSourceConfigurationSchema";
    export * from "./leading";
    

    but this is really fragile, so I wouldn't recommend it.