Skip to content

Commit 8640897

Browse files
committed
test: add isZodType tests for v4
1 parent c5f03f6 commit 8640897

File tree

2 files changed

+73
-22
lines changed

2 files changed

+73
-22
lines changed

src/__tests__/zod4.test.ts

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -72,27 +72,62 @@ describe('isZodTypeLike', () => {
7272
});
7373

7474
describe('isZodType', () => {
75-
// since we use require here, the prototype chain in the cjs module will be different than the esm build
76-
const { z: z3_2 } = require('zod/v3') as typeof import('zod/v3');
75+
describe('v3', () => {
76+
// since we use require here, the prototype chain in the cjs module will be different than the esm build
77+
const { z: z3_2 } = require('zod/v3') as typeof import('zod/v3');
7778

78-
it('should return true for an instance of ZodNumber', () => {
79-
expect(isZodType(z3.number(), { version: 3 })).toBe(true);
79+
it('should return true for an instance of ZodNumber', () => {
80+
expect(isZodType(z3.number(), { version: 3 })).toBe(true);
81+
});
82+
it('should return true for an instance of ZodObject', () => {
83+
expect(isZodType(z3.object({}), { version: 3 })).toBe(true);
84+
});
85+
it('should return false for null', () => {
86+
expect(isZodType(null, { version: 3 })).toBe(false);
87+
});
88+
it('should return false for any empty object', () => {
89+
expect(isZodType({}, { version: 3 })).toBe(false);
90+
});
91+
it('should return false for an object with a null prototype', () => {
92+
expect(isZodType(Object.create(null), { version: 3 })).toBe(false);
93+
});
94+
it('should return false for a Zod v4 object', () => {
95+
expect(isZodType(z4.object({}), { version: 3 })).toBe(false);
96+
});
97+
it('should return true for a ZodObject created in a different context', () => {
98+
const input = z3_2.object({});
99+
expect(input).not.toBeInstanceOf(z3.ZodType);
100+
expect(isZodType(input, { version: 3 })).toBe(true);
101+
});
80102
});
81-
it('should return true for an instance of ZodObject', () => {
82-
expect(isZodType(z3.object({}), { version: 3 })).toBe(true);
83-
});
84-
it('should return false for null', () => {
85-
expect(isZodType(null, { version: 3 })).toBe(false);
86-
});
87-
it('should return false for any empty object', () => {
88-
expect(isZodType({}, { version: 3 })).toBe(false);
89-
});
90-
it('should return false for an object with a null prototype', () => {
91-
expect(isZodType(Object.create(null), { version: 3 })).toBe(false);
92-
});
93-
it('should return true for a ZodObject created in a different context', () => {
94-
const input = z3_2.object({});
95-
expect(input).not.toBeInstanceOf(z3.ZodType);
96-
expect(isZodType(input, { version: 3 })).toBe(true);
103+
describe('v4', () => {
104+
it('should return true for an instance of ZodNumber', () => {
105+
expect(isZodType(z4.number(), { version: 4 })).toBe(true);
106+
});
107+
it('should return true for an instance of ZodObject', () => {
108+
expect(isZodType(z4.object({}), { version: 4 })).toBe(true);
109+
});
110+
it('should return false for null', () => {
111+
expect(isZodType(null, { version: 4 })).toBe(false);
112+
});
113+
it('should return false for any empty object', () => {
114+
expect(isZodType({}, { version: 4 })).toBe(false);
115+
});
116+
it('should return false for a Zod v3 object', () => {
117+
expect(isZodType(z3.object({}), { version: 4 })).toBe(false);
118+
});
119+
it('should return true for a ZodObject created in a different context', () => {
120+
const base = z4.object({});
121+
const input = {
122+
_zod: {
123+
version: structuredClone(base._zod.version)
124+
},
125+
'~standard': {
126+
vendor: base['~standard'].vendor
127+
}
128+
};
129+
expect(input).not.toBeInstanceOf(z4.ZodType);
130+
expect(isZodType(input, { version: 4 })).toBe(true);
131+
});
97132
});
98133
});

src/zod4.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { z as z3 } from 'zod/v3';
2+
import type { z as z4 } from 'zod/v4';
23

34
import { isObject, isObjectLike, isPlainObject } from './object.js';
45

@@ -30,6 +31,7 @@ type ZodSafeParseErrorLike = {
3031
type ZodSafeParseResultLike<T> = ZodSafeParseErrorLike | ZodSafeParseSuccessLike<T>;
3132

3233
type ZodTypeLike<TOutput, TInput = unknown> = {
34+
[key: string]: any;
3335
readonly _input: TInput;
3436
readonly _output: TOutput;
3537
safeParseAsync: (data: unknown) => Promise<ZodSafeParseResultLike<TOutput>>;
@@ -55,8 +57,22 @@ function isZodV3Type(arg: unknown): arg is z3.ZodTypeAny {
5557
return Boolean(prototype && Reflect.get(prototype, 'name') === 'ZodType');
5658
}
5759

58-
function isZodType(arg: unknown, _options: { version: 3 }): arg is z3.ZodTypeAny {
59-
return isZodV3Type(arg);
60+
function isZodV4Type(arg: unknown): arg is z4.ZodType {
61+
if (!isZodTypeLike(arg)) {
62+
return false;
63+
}
64+
const zod: unknown = arg._zod;
65+
if (!isPlainObject(zod)) {
66+
return false;
67+
}
68+
const version: unknown = zod.version;
69+
return isPlainObject(version) && version.major === 4;
70+
}
71+
72+
function isZodType(arg: unknown, options: { version: 3 }): arg is z3.ZodTypeAny;
73+
function isZodType(arg: unknown, options: { version: 4 }): arg is z4.ZodType;
74+
function isZodType(arg: unknown, options: { version: 3 | 4 }): boolean {
75+
return options.version === 3 ? isZodV3Type(arg) : isZodV4Type(arg);
6076
}
6177

6278
export { isZodType, isZodTypeLike };

0 commit comments

Comments
 (0)